MySQL запрос становится медленнее, когда используется индекс - PullRequest
0 голосов
/ 08 октября 2019

Я дошел до того, что не могу понять, почему следующий запрос MySQL становится медленнее, когда я использую индекс в предложении where. Колонка, которая сводит меня с ума, называется удаленной. Таблица содержит 4,8 млн строк.

Запрос:

SELECT SQL_NO_CACHE SUM(amount)/100 FROM transactions WHERE (type="Payment" or type="Refund") and deleted is NULL

Этот запрос занимает чуть более 11 секунд, когда столбец является индексом, и 3 секунды, когда его нет. индексируется или когда я использую USE INDEX(), которые говорят оптимизатору не использовать индекс.

MySQL версии 5.6, протестировано в AWS Aurora db.r5.xlarge (4CPU / 32GB)

Структура таблицы:

id int(11) NOT NULL, type enum('Charge','Payment','Refund','Credit Adjustment','Debit Adjustment','Transfer') NOT NULL, amount int(11) NOT NULL, deleted datetime DEFAULT NULL, deleted_by int(11) DEFAULT NULL ENGINE=InnoDB DEFAULT CHARSET=utf8; ADD KEY type (type), ADD KEY deleted (deleted)

Я быцените любые подсказки здесь!

Ответы [ 4 ]

1 голос
/ 09 октября 2019

Я использовал «объяснение», чтобы проверить вышеуказанный запрос, можно ли использовать индекс или нет. В результате мой индекс не работает ни для оператора «ИЛИ», ни для «IN», поэтому я считаю, что «UNION» - лучший выбор. И я думаю, вам не нужно добавлять индекс для «удаленного» столбца, потому что он также не работает.

«объяснить» результат для оператора IN:

«объяснить» результат для оператора ИЛИ:

«объединить» результат:

индекс по «удаленному» столбцу не работает: index on

0 голосов
/ 15 октября 2019

Если вы удалите индекс только на deleted и добавите этот «составной» индекс:

INDEX(deleted, type)   -- in this order

он может работать быстрее. Обратите внимание, что сначала стоит столбец = (считается IS NULL), затем IN (в который превращается ваш OR).

Еще быстрее сделать индекс «охватывающим»:

INDEX(deleted, type, amount)   -- in this order

Превращение OR в UNION - хороший трюк, но здесь это не обязательно.

Если deleted редко NULL, тогда Оптимизатор может предпочесть этот индекс,даже если это окажется менее эффективным. (Это может объяснить проблему, которую вы представляете. Мой составной индекс позволяет избежать проблемы.)

Независимая проблема: почему deleted? Разве вы не можете просто иметь deleted_by, являющуюся NULL, чтобы обозначить то же самое?

0 голосов
/ 09 октября 2019

Думаю, у меня возникла логическая идея, почему использование индексированного столбца может вызвать задержку. Проблема должна заключаться в данных этого столбца и особенно в его сильно искаженном распределении уникальных значений - соответственно двоичных трех узлов. Он состоит из 4,8 млн строк с одинаковым значением NULL и всего 30 тыс строк с 3 тыс уникальных значений.

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

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

0 голосов
/ 08 октября 2019

(Редактировать: Очевидно, что это не так для данной конкретной ситуации. Этот ответ применяется только в том случае, если условия ИЛИ затрагивают различные поля .... или создать проверку диапазона, которая препятствует использованию полей дальше в индексе. См. Комментариидля деталей.)

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

SELECT a FROM b WHERE y = n1 OR y = n2

, расширив его до такого типа объединения

SELECT a FROM b WHERE y = n1
UNION 
SELECT a FROM b WHERE y = n2

Я слышал, что в более поздних версиях такие условия выражались в виде y IN (n1, n2) немного более эффективно, но моя основная работа в последние несколько лет была в MS SQL, поэтому я не могу сказать, насколько он улучшился.

Это может даже использоваться в случае вашейпрямое суммирование с немного большим расширением ....

SELECT SUM(subt) 
FROM (
   SELECT SUM(amount)/100 AS subt FROM transactions WHERE type="Payment" and deleted is NULL
   UNION 
   SELECT SUM(amount)/100 AS subt FROM transactions WHERE type="Refund" and deleted is NULL
) AS subq
...