Почему MySQL может выполнять LIKE в индексе, когда в индексе есть какой-то ведущий подстановочный знак, когда используется «cover-index»? - PullRequest
0 голосов
/ 29 мая 2019

Это пример "High Performance MySQL 3rd".

mysql> EXPLAIN SELECT * FROM products WHERE actor='SEAN CARREY' AND title like '%APOLLO%';

В книге сказано, что MySQL не может выполнить LIKE, как показано ниже.

MySQL не может выполнить операцию LIKE в индексе. Это ограничение API низкоуровневого механизма хранения, который в MySQL 5.5 и ранее допускаются только простые сравнения (такие как равенство, неравенство, и больше чем) в индексных операциях. MySQL может выполнять сопоставление префиксов Как шаблоны в индексе, потому что он может преобразовать их в простые сравнения, но ведущий подстановочный знак в запросе делает это невозможным для механизма хранения, чтобы оценить совпадение. Таким образом, сервер MySQL Самому придется выбирать и сопоставлять значения строки, а не значения индекса.

После этого книга дала улучшение "отложенного соединения".

mysql> EXPLAIN SELECT * FROM products
-> JOIN (
-> SELECT prod_id FROM products WHERE actor='SEAN CARREY' AND title LIKE '%APOLLO%'
-> ) AS t1 ON (t1.prod_id=products.prod_id);

Даже (actor, title, prod_id) является «индексом покрытия», MySQL также не может выполнить LIKE в индексе.

Я так растерялся!

1 Ответ

0 голосов
/ 29 мая 2019

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

Основная проблема заключается в том, что индекс покрытия в MySQL 5.5 технически не совсем соответствует тому, что, как вы предполагаете, он делает (и мог бы).

Чтобы правильно прочитать цитируемое утверждение из книги, вы должны знать, что существует разница между MySQL-сервером и базовым механизмом хранения . Сервер MySQL принимает ваш запрос, решает, как его выполнить, отправляет запрос в механизм хранения (InnoDB) через API и возвращает несколько строк.

Итак, для вашего первого запроса MySQL просит InnoDB предоставить ему следующие данные: все столбцы (select *), используя индекс для поиска actor='SEAN CARREY'. Хотя было бы неплохо, и вы предполагаете, что индекс покрытия будет делать это, к сожалению, он также не может напрямую удалять строки на основе title like '%APOLLO%', потому что

Это ограничение API низкоуровневого механизма хранения, который в MySQL 5.5 и более ранних версиях допускает только простые сравнения (такие как равенство, неравенство и больше, чем) в операциях с индексами.

Поскольку вы запросили *, он извлекает все столбцы, для которых требуется просмотреть данные таблицы, для всех строк с правильным актером (используя индекс) из механизма InnoDB, а затем фильтрует их впоследствии, так как

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

Во втором запросе серверу MySQL требуются только prod_id (согласно запросу) и title (для сравнения where) из механизма хранения. Это теперь фактически покрыто индексом! Хотя верхний уровень все еще должен выполнять оценку на title like '%APOLLO%', ядру хранилища теперь не нужно считывать фактические данные таблицы, чтобы выполнить запрос для подзапроса. .

Сервер MySQL теперь может оценивать полученные данные и отправлять еще один запрос в механизм хранения, чтобы получить все столбцы для prod_id, которые удовлетворяют условию where. В крайних случаях это может вообще не фильтроваться (например, каждая строка с actor='SEAN CARREY' может также выполнить title like '%APOLLO%'), и тогда отложенное объединение может быть немного медленнее, поскольку в целом вы выполняете больше работы.

Вы думаете, что это не то, что должен делать индекс покрытия? Вы правы. И MySQL 5.6 научился делать это правильнее :

Индекс условия Pushdown (ICP) - это оптимизация для случая, когда MySQL извлекает строки из таблицы, используя индекс. Без ICP механизм хранения просматривает индекс, чтобы найти строки в базовой таблице, и возвращает их на сервер MySQL, который оценивает условие WHERE для строк. С включенным ICP, и если части условия WHERE могут быть оценены с использованием только столбцов из индекса, сервер MySQL передает эту часть условия WHERE в механизм хранения.

[...]

MySQL может использовать индекс для сканирования людей с zipcode='95054'. Вторая часть (lastname LIKE '%etrunia%') не может быть использована для ограничения количества строк, которые должны быть отсканированы, поэтому без опускания условия индекса этот запрос должен получить полные строки таблицы для всех людей, у которых zipcode='95054'.

При нажатии на условие индекса MySQL проверяет часть lastname LIKE '%etrunia%' перед чтением полной строки таблицы. Это позволяет избежать чтения полных строк, соответствующих кортежам индекса, которые соответствуют условию zipcode, но не условию lastname.

Поскольку это было необходимо только для решения технических проблем, вам больше не нужно защищенное соединение (хотя вы не должны забывать об этом, это может быть полезно в других ситуациях). Ваш вывод объяснения для вашего первого запроса теперь должен включать

  • Using index condition (свойство JSON: using_index_condition)

Таблицы читаются путем обращения к индексным кортежам и их предварительного тестирования, чтобы определить, следует ли читать полные строки таблицы.Таким образом, информация индекса используется для того, чтобы отложить («нажать вниз») чтение полных строк таблицы, если в этом нет необходимости.См. Раздел 8.2.1.5, «Оптимизация нажатия на условия индексации».

...