MySQL EXPLAIN показывает, что ключ не используется. Он вообще что-нибудь делает? - PullRequest
0 голосов
/ 27 апреля 2020

Допустим, у меня есть три таблицы: отгрузки , клиенты и магазины . Таблица отгрузки имеет два индекса: customer_id типа INT (ссылается на таблицу клиентов) и date типа datetime. Таблица покупателей имеет один индекс: store_id типа INT (ссылается на таблицу магазина).

Если я фильтрую отгрузки по дате, я вижу дату используемый индекс:

EXPLAIN extended SELECT * FROM shipments
WHERE date >= '2020-04-01' AND date <= '2020-05-01';

+----+-------------+-----------+-------+---------------+------+---------+-------+--------+----------+-------------+
| id | select_type | table     | type  | possible_keys | key  | key_len | ref   | rows   | filtered | Extra       |
+----+-------------+-----------+-------+---------------+------+---------+-------+--------+----------+-------------+
|  1 | SIMPLE      | shipments | range | date          | date | 9       | NULL  | 250796 |   100.00 | Using where |
+----+-------------+-----------+-------+---------------+------+---------+-------+--------+------------------------+

Однако вывод для следующих двух запросов смущает меня, потому что он в значительной степени совпадает:

EXPLAIN extended SELECT shipments.* FROM shipments
LEFT JOIN customers ON shipments.customer_id = customers.id
WHERE customers.store_id = 100 AND 
shipments.date >= '2020-04-01 00:0:00.0' AND shipments.date <= '2020-05-01 00:0:00.0';

+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
| id | select_type | table     | type  | possible_keys     | key         | key_len | ref           | rows   | filtered | Extra                    |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
|  1 | SIMPLE      | customers | ref   | PRIMARY, store_id | store_id    | 5       | const         | 38     |   100.00 | Using where; Using index |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
|  1 | SIMPLE      | shipments | ref   | customer_id, date | customer_id | 5       | customers.id  | 663    |   100.00 | Using where              |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+-------------------------------------+

EXPLAIN extended SELECT shipments.* FROM shipments
LEFT JOIN customers ON shipments.customer_id = customers.id
WHERE customers.store_id = 100;

+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
| id | select_type | table     | type  | possible_keys     | key         | key_len | ref           | rows   | filtered | Extra                    |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
|  1 | SIMPLE      | customers | ref   | PRIMARY, store_id | store_id    | 5       | const         | 38     |   100.00 | Using where; Using index |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+----------+--------------------------+
|  1 | SIMPLE      | shipments | ref   | customer_id       | customer_id | 5       | customers.id  | 663    |   100.00 | Using where              |
+----+-------------+-----------+-------+-------------------+-------------+---------+---------------+--------+-------------------------------------+

Вопрос № 1 : означает ли этот вывод, что первый из этих двух запросов вообще не использует индекс date ? Я читал, что MySQL не будет использовать более одного индекса на таблицу, так что мой индекс date вообще влияет на производительность? (в моей программе все запросы, которые фильтруются по диапазону дат, выглядят примерно так же) Если предположить, что одновременно запускаются тонны клиентов, тонны отгрузок и тонны запросов, как мне go повышение производительности?

Вопрос №2 : Почему значение «строк» ​​в выходных данных одинаково для этих двух запросов, если первый подразумевает больше фильтрации, чем первый? Разве это не должно быть иначе? Очевидно, я не понимаю этого должным образом, так что, может, кто-нибудь объяснит мне это?

Заранее спасибо!

Примечание. Это mysql 5.5.56, а таблицы InnoDB.

Ответы [ 2 ]

2 голосов
/ 27 апреля 2020

1) Да, он фильтруется по customer.store_id, а затем выполняет обратное объединение с таблицей отгрузок на основе customer_id.

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

2) Поскольку это оценка, основанная на статистике индекса, в основном на количестве элементов каждого индекса.

1 голос
/ 28 апреля 2020

Это на самом деле не LEFT объединение, так как вам требуется store_id = 100. Это не изменит производительность; Оптимизатор уже разобрался. (Это помогает читателям попытаться выяснить цель запроса.)

Вы говорите SELECT *. Если вам не нужны все столбцы, не просите их всех. Если какой-либо столбец является большим TEXT, текст находится в блоке «off-record», который требует усилий для извлечения.

INDEX(customer_id), INDEX(date) не так хорош, как «составной» INDEX(customer_id, date) При этом он может сосредоточиться на записях для этого одного клиента и сканировать нужные даты. Это может сделать улучшение скорости. Примечание: важен порядок столбцов в этом индексе - сначала поставьте столбцы = (customer_id), а диапазон (date >=...) последним.

(Q1) MySQL (за редким исключением) не использует более одного индекса за раз. Вы фильтруете shipments по двум вещам: customer_id и date, а не только date. С другой стороны, в этом запросе будет использоваться INDEX(date), а , а не будет использоваться составной индекс, приведенный выше: SELECT * FROM shipments where date >= CURDATE(); (Это позволяет получить всю информацию обо всех отгрузках на сегодняшний день для всех клиентов.

Примечание: вы включаете полночь с обоих концов. Измените последнее сравнение с <= на <.

(Q2) Числа в EXPLAIN являются оценочными. на «статистику» и «исследования», которые не обязательно являются очень точными. Кроме того, в некоторых случаях некоторые советы игнорируются. Явное упущение: LIMIT.

Остерегайтесь использования USE INDEX и FORCE INDEX. Если Вы чувствуете потребность в этом, возможно, вы упускаете что-то важное. Если вы его используете, «это может помочь сегодня, но ухудшит ситуацию завтра, когда изменится распределение данных.

Совет: для сравнения с DATE / DATETIME / DATETIME (1) / TIMESTAMP, время полуночи может опускаться из части 'time': '2020-05-01' работает так же, как '2020-05-01 00:0:00.0'

Версия 5.5? Это довольно старый. 5.6 добавляет EXPLAIN FORMAT=JSON, что даст больше информации - подробности об использовании индекса, сортировке, query_cost и т. д. c.

«Этот материал по оптимизации все еще довольно неясен для меня.» - Да. И MySQL имеет один из более простых оптимизаторов.

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