Как можно ускорить выполнение SQL запроса?

MySQL запрос:
select `product_id`, count(*) as `cnt`
from `product_prop`
where `prop_id` in (1,2,3,...)
group by `product_id`
order by `cnt` desc


В таблице две колонки `product_id` и `prop_id`.
Индексы: product_id, prop_id, product_id+prop_id уникальный
В таблице 85М записей, будет больше, и если `prop_id` in (1,2,3,...) штук 20, может быть и больше, запрос выполняется 18 секунд, что прям совсем много. Если убрать сортировку, то выполняется моментально, но без неё никак. Пробовал having, но то же самое, что, как бы, и логично.
  • Вопрос задан
  • 2267 просмотров
Пригласить эксперта
Ответы на вопрос 7
@Akina
Сетевой и системный админ, SQL-программист.
если `prop_id` in (1,2,3,...) штук 20, может быть и больше, запрос выполняется 18 секунд, что прям совсем много.

Критичное количество значений в таком списке - порядка 7..10, дальше действительно запрос резко замедляется.

Workaround: создаём индексированную временную таблицу в памяти, записываем в неё набор значений, используем в запросе:

CREATE TEMPORARY TABLE criteria (prop_id INT PRIMARY KEY) ENGINE = Memory
SELECT prop_id FROM (VALUES ROW(1), ROW(2), ROW(3), ...) criteria (prop_id);

и потом

select `product_id`, count(*) as `cnt`
from `product_prop`
NATURAL JOIN criteria 
group by `product_id`
order by `cnt` desc


Ну а ускорить ORDER BY по вычисляемому полю - вообще без шансов.
Ответ написан
@402d
начинал с бейсика на УКНЦ в 1988
как понимаю это сортировка товаров от наиболее подходящих до обладающих 1м из признаков.
Сделай вспомогательную таблицу статистики сколько товаров всего с заданным проп_ид.
обновление по тригерам.

потом смотришь в нее и береш максимальный из (1,2,3...) например 3
Далее запрос будет из двух проекций (A и B) на твою таблицу
select product_id from `product_prop` A where prop_id=3 (этим мы уходим от фулскана по 85М)
и добавляем подзапрос посчитать сколько совпало осталных свойств
select product_id,(... подзапрос .....) from `product_prop` A
Ответ написан
yesbro
@yesbro
Думаю, помогаю думать
План запроса, пожалуйста :)

Результат запроса полностью помешается в память? peak_memory_used (из EXPLAIN filesort_summary) больше чем sort_buffer_size из настроек базы?

Запрос похоже никак не оптимизировать. Вот обсуждение подобной проблемы https://stackoverflow.com/questions/75306623/order...

Надо или тюнить mysql или думать над каким-то кешированием/промежуточными расчетами (хотя пока не вижу что тут можно кешировать).
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
Можно материализовать. Перечислить все комбинации prop_id и создать такое представление

prop_ids | responce
---------|---------
'1,2,3'  | [ { "prod_001" : 200 }, { "prod_333" : 134 } , ..... ]
'2,3,4'  | [.....]
Ответ написан
LaRN
@LaRN
Senior Developer
Может индекс поменять с product_id+prop_id на
prop_id + product_id. Отбор же по prop_id идет, а тут не попадание в первое поле индекса.
Ответ написан
@dedmaks
На мой взгляд, сугубо поверхностный , без разбивки таблицы на части ( Аля продукт до 1м, 2м и т.д.), хранимок или переноса части логики касательно группировки, подсчёта на бек не обойтись для ускорения.
Ответ написан
Комментировать
@dmitryklerik
Для выполнения группировки создаётся временная таблица, а уже потом происходит сортировка по этой временной таблице. Группировка очень быстрая так как по индексу, а сортировка медленная так как в созданной временной таблице никаких индексов для сортировки уже нет и приходится сортировать без индексов (Using filesort). Вот что могу посоветовать:

1) Убедится что временная таблица создаётся в памяти, SHOW STATUS LIKE 'Created_tmp_disk_tables' не должно расти.
2) Убедится что сортировка происходит без создания временных файлов на диске. Для этого нужно убедится что SHOW STATUS LIKE 'Sort_merge_passes'; не растёт быстро. Если растёт то перед запросом сессионно прибавьте значение настройки sort_buffer_size.
3) Лучшее решение: записей у вас много, лучше придумать другую структуру данных чтобы отказаться от этого запроса.

PS На всякий случай приведите вывод EXPLAIN и EXPLAIN ANALYZE когда у вас prop_id` in (1,2,3,...) штук 20, интересно из-за чего увеличивается время запроса: из-за роста кол-ва записей которые попадают в выборку или из-за того что в какой-то момент MySQL решает что full table scan выгодней и отказывается от использования индекса
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы