2015-11-25 4 views
3

Очень простая проблема, пока сложно найти решение. Адресная таблица с 2,498,739 строк имеет поле min_ip и max_ip. Это основные привязки таблицы для фильтрации.MySQL не всегда использует индекс

Запрос очень прост.

SELECT * 
FROM address a 
WHERE min_ip < value 
    AND max_ip > value; 

Поэтому логично создать индекс для min_ip и max_ip, чтобы сделать запрос быстрее.

Указатель, созданный для следующего.

CREATE INDEX ip_range ON address (min_ip, max_ip) USING BTREE; 
CREATE INDEX min_ip ON address (min_ip ASC) USING BTREE; 
CREATE INDEX max_ip ON address (max_ip DESC) USING BTREE; 

я пытаюсь создать только первый вариант (сочетание min_ip и max_ip), но это не сработало, так что я подготовил по крайней мере 3 индексов, чтобы дать MySQL больше возможностей для выбора индекса. (Обратите внимание, что эта таблица очень много статическая и больше справочной таблицы)

+------------------------+---------------------+------+-----+---------------------+-----------------------------+ 
| Field     | Type    | Null | Key | Default    | Extra      | 
+------------------------+---------------------+------+-----+---------------------+-----------------------------+ 
| id      | bigint(20) unsigned | NO | PRI | NULL    | auto_increment    | 
| network    | varchar(20)   | YES |  | NULL    |        | 
| min_ip     | int(11) unsigned | NO | MUL | NULL    |        | 
| max_ip     | int(11) unsigned | NO | MUL | NULL    |        | 
+------------------------+---------------------+------+-----+---------------------+-----------------------------+ 

Теперь она должна быть прямой, чтобы запросить таблицу с min_ip и max_ip в качестве критериев фильтра.

EXPLAIN 
SELECT * 
FROM address a 
WHERE min_ip < 2410508496 
    AND max_ip > 2410508496; 

Запрос выполнил что-то около 0.120-0.02 сек. Однако при тестировании нагрузки запрос быстро ухудшает производительность. Сервер MySQL использует ракеты Sky для 100% использования процессора только на нескольких одновременных запросах и производительности, что быстро ухудшается и не масштабируется. Медленный запрос на сервере mysql был включен с 10 секундами или выше, и в итоге запрос select появляется в журналах сразу после нескольких секунд теста нагрузки. Итак, я проверил запрос с объяснением и узнал, что он не использовал индекс.

объяснить план результата

id select_type table type possible_keys   key  key_len ref  rows Extra   
------ ----------- ------ ------ ---------------------- ------ ------- ------ ------- ------------- 
    1 SIMPLE  a  ALL  ip_range,min_ip,max_ip (NULL) (NULL) (NULL) 2417789 Using where 

Интересно, что это было в состоянии определить ip_range, ip_min и ip_max как потенциальные индексы, но никогда не использовать какой-либо из него, как показано в ключевом столбце. Я знаю, что могу использовать FORCE INDEX и пытаюсь использовать план объяснений.

EXPLAIN 
SELECT * 
FROM address a 
FORCE INDEX (ip_range) 
WHERE min_ip < 2410508496 
    AND max_ip > 2410508496; 

Объяснить план с FORCE INDEX результатом

id select_type table type possible_keys key  key_len ref  rows Extra     
------ ----------- ------ ------ ------------- -------- ------- ------ ------- ----------------------- 
    1 SIMPLE  a  range ip_range  ip_range 4  (NULL) 1208894 Using index condition 

С FORCE INDEX, да он использует индекс ip_range как ключ, а строка показывает подмножество из запроса, который не использует FORCE INDEX, который 1208894 с 2,417,789. Так что определенно, использование индекса должно иметь лучшую производительность. (Если я не понял результат объяснения)

Но, что более интересно, после нескольких тестов я узнал, что в некоторых случаях MySQL использует индекс даже без FORCE INDEX. И мое наблюдение, когда значение невелико, оно использует индекс.

EXPLAIN 
SELECT * 
FROM address a 
WHERE min_ip < 508496 
    AND max_ip > 508496; 

Объяснить Результат

id select_type table type possible_keys   key  key_len ref  rows Extra     
------ ----------- ------ ------ ---------------------- -------- ------- ------ ------ ----------------------- 
    1 SIMPLE  a  range ip_range,min_ip,max_ip ip_range 4  (NULL)  1 Using index condition 

Таким образом, это просто озадачило меня, что база по значению пропуском в запросе на выборку, MySQL решает, когда использовать индекс и если не использовать индекс. Я не могу представить, в чем заключается основа для определения того, когда использовать индекс для определенного значения, передаваемого запросу.Я понимаю, что индекс не может быть использован, если в условии WHERE нет подходящего индекса, но в этом случае очень явный индекс ip_range, который является индексом, основанным на столбцах min_ip и max_ip, подходит для условия WHERE в этом случае.

Но большая проблема у меня есть, а как насчет других запросов. Должен ли я пойти и проверить эти запросы в большом масштабе. Но даже тогда, по мере роста данных, могу ли я рассчитывать и ожидать от MySQL использования индекса? Да, я всегда могу использовать FORCE INDEX, чтобы убедиться, что он использует индекс. Но это не стандартный SQL, который работает во всей базе данных. Структуры ORM, возможно, не смогут поддерживать синтаксис FORCE INDEX при генерации SQL и тесно связывают ваш запрос с вашими именами индексов.

Не уверен, что кто-либо когда-либо сталкивался с этой проблемой, но это кажется очень большой проблемой для меня.

+4

Это выглядит как проблема с мощностью. Когда возвращаемые строки составляют около 30% или более таблицы, mysql решит, что сканирование таблицы лучше, игнорируя индекс. Индексы полезны только для возвращения небольшой доли строк. первый запрос возвращает 1208894 строку вашей второй только 1 строка – Mihai

+2

MySQL не будет использовать индекс, если его мощность низкая. Кардинальность - это число, которое сообщает вам, сколько уникальных значений есть в наборе данных. Мощность PK равна 1, это максимальное значение. MySQL делает это как шаг оптимизации. Он определяет, уменьшает ли индекс необходимый объем поиска. – Mjh

+0

Спасибо за быстрый ответ. Мне кажется, что это специфично для реализации MySQL. Не уверен, если я буду следить за мощностью, но как MySQL определяет мощность? Когда я использую значение 2,410,508,496, он не использует индекс, когда я использую 508,496, он использует индекс, который довольно странный. – Itherael

ответ

2

Полностью согласен с Ватевым и другими. Это делает не только MySQL. Сканирование таблицы иногда дешевле, чем просмотр индекса, а затем поиск соответствующих записей на диске.

Единственный момент, когда он обязательно использует индекс, является индексом покрытия, что означает, что каждый столбец запроса (для этой конкретной таблицы, конечно) присутствует в индексе. Значение, если вам нужно, например, только колонка сети

SELECT network 
FROM address a 
WHERE min_ip < 2410508496 
    AND max_ip > 2410508496; 

затем индекс покрытия, как

CREATE INDEX ip_range ON address (min_ip, max_ip, network) USING BTREE; 

будет смотреть только на индекс, поскольку нет никакой необходимости для поиска дополнительных данных на диске вообще. И весь индекс можно сохранить в памяти.

+0

Это в значительной степени решает проблему. Я думал, почему сканирование таблицы было более эффективным, чем использование индекса и @fancyPants, вы просто прибивали это объяснение. У индекса нет всех данных, потому что у моего запроса есть SELECT * (требуется дополнительные данные, которые не указаны в индексе), и все равно нужно будет извлечь эти данные из таблицы. Поэтому, несмотря на то, что индекс может фильтровать записи, записи, возвращаемые фильтром, по-прежнему будут доступны из таблицы, которая может быть случайным доступом IO (переключение между блоками данных). Таким образом, доступ ко всей таблице последовательно будет более эффективным. – Itherael

+0

Это нормально, пока индекс не поместится в памяти. –

0

Диапазоны, подобные этому, неприятны для оптимизации. Но у меня a technique. Он требует неперекрывающихся диапазонов и хранит только start_ip, а не end_ip (который эффективно доступен из «следующей» записи). Он предоставляет хранимые процедуры, чтобы скрыть грязный код, включая ORDER BY ... LIMIT 1 и другие трюки. Для большинства операций он не будет ударять более одного блока данных, в отличие от очевидных подходов, которые имеют тенденцию извлекать половину или всю таблицу.

0

Я согласен со всеми ответами выше. но вы можете попробовать сделать только один композитный индекс, как это:

create index ip_rang on address (min_ip ASC,max_ip DESC) using BTREE; 

Как вы знаете, индекс также имеет тот недостаток, используя дисковое пространство, чтобы рассмотреть оптимальный показатель для использования.

Смежные вопросы