Почему запрос с меньшим количеством строк для сканирования (согласно объяснению) на самом деле выполняется значительно медленнее, чем запрос с гораздо большим количеством строк? - PullRequest
0 голосов
/ 16 января 2019

TL; DR
При запросе к той же таблице, почему первый запрос ниже (сканирует 20 строк) занимает значительно больше времени, чем второй (сканирует 35k + строк)?

Первый запрос:

id  select_type     table       type         possible_keys     key                            key_len     ref     rows    Extra
1   SIMPLE          Groups      range        <lots of keys>    group_name_ip_address_idx      317         NULL    20      Using index condition; Using where

Второй запрос:

id  select_type     table       type         possible_keys     key                    key_len     ref     rows    Extra
1   SIMPLE          Groups      ref_or_null  <lots of keys>    email_address_idx      768         const   35415   Using index condition; Using where


Я использую «строки» в объяснении как прямой индикатор производительности запроса (что может быть не так?), Но все же запрос с 20 строками занимает гораздо больше времени, чем смущает запрос 35k. Не специалист по SQL, кто-нибудь может объяснить мне, что может вызвать это?


Более длинная версия:
У меня есть таблица «Группы», в которой есть поле «имя_группы» и еще 20 полей («field_1», «field_2», ..., «field_20») с информацией о клиенте.

Я использую эту таблицу, чтобы определить, когда я вижу информацию о клиенте, какой группе (группам) он соответствует. Для фиктивного примера, если в таблице есть запись, в которой имя группы равно «US male», а все 20 полей имеют нулевое значение, за исключением «гражданства» «US» и «пол» «male», то это означает, что когда я вижу клиент с гражданством "США" и полом "мужчина", он соответствует группе "мужчина США".

Я использую этот запрос (Query1), который занимает 3 ~ 5 мс для достижения этой цели:

select * from Groups 
where group_name = "US male" 
  and (field_1 = "something1" or field_1 is null) 
  and (field_2 = "something2" or field_2 is null) 
  and ... and (field_20 = "something20" or field_20 is null)

Эти "что-то" представляют информацию о текущем клиенте, которую я хочу знать, с какими группами он / она соответствует. Так что если этот запрос что-то возвращает, это означает совпадение; в противном случае не совпадают.

Объяснить вывод вышеуказанного запроса:

id  select_type     table       type         possible_keys     key                               key_len     ref           rows    Extra
1   SIMPLE          Groups      ref_or_null  <lots of keys>    group_name_email_address_idx      962         const,const   2       Using index condition; Using where

Обратите внимание, что один клиент может соответствовать нескольким группам, поэтому для N имени группы мне понадобится N запросов, как указано выше. Теперь, когда N становится больше, я хочу использовать один отдельный запрос вместо N маленьких запросов, чтобы сделать то же самое, и вот где я застрял.


Сначала я попытался удалить условие group_name = "XXXX" в where - выбрать все подходящие группы вместо проверки по одной (Query2).

select * from Groups 
where (field_1 = "something1" or field_1 is null) 
  and (field_2 = "something2" or field_2 is null) 
  and ... and (field_20 = "something20" or field_20 is null)

объяснить выходы:

id  select_type     table       type         possible_keys     key                    key_len     ref     rows    Extra
1   SIMPLE          Groups      ref_or_null  <lots of keys>    email_address_idx      768         const   35415   Using index condition; Using where

Это медленно (~ 70 мс), потому что он не может использовать индексы, которым нужно имя группы, которые являются наиболее эффективными, потому что имя_группы имеет наименьшее количество элементов. (количество строк, необходимых для сканирования, составляет 35 КБ по сравнению с 2 в первом запросе). Так что это не очень хорошо сработало.


Затем, чтобы запрос использовал индексы имен групп, я добавил group_name in (<all group names>) в where (Query3):

select * from Groups 
where group_name in ("group1", "group2", ..., "groupN") 
  and (field_1 = "something1" or field_1 is null) 
  and (field_2 = "something2" or field_2 is null) 
  and ... and (field_20 = "something20" or field_20 is null)

объяснить выходы:

id  select_type     table       type         possible_keys     key                            key_len     ref     rows    Extra
1   SIMPLE          Groups      range        <lots of keys>    group_name_ip_address_idx      317         NULL    20      Using index condition; Using where

Я увидел, что нужно отсканировать 20 строк, что значительно лучше, чем 35415, поэтому я ожидал, что он будет работать быстро. Однако когда я попытался запустить его, на самом деле это заняло гораздо больше времени, чем Query2 (примерно в 20 раз), который сломал мой сервис.


После всего этого я очень растерялся, почему запрос, который сканирует 20 строк, занимает намного больше времени, чем запрос, который сканирует 35 тыс. Строк? Я неправильно читаю выводы объяснения?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...