Я хочу реализовать очень распространенную функцию - фильтрацию некоторых элементов по тегу. В Интернете есть много учебных пособий с примерами того, как это сделать. Запрос довольно прост и быстр (при условии наличия соответствующих индексов).
Но обычно отфильтрованные элементы нужно сортировать по некоторому полю. Например, когда вы фильтруете вопросы по тегу на SO, вы получаете результаты сортировки.
Чтобы выполнить эту задачу (при условии, что нам нужно сортировать по рейтингу), можно написать:
SELECT item.id FROM item
INNER JOIN taggeditem ON taggeditem.item_id = item.id
WHERE
taggeditem.tag_id = 1234
ORDER BY item.rating DESC
У нас есть индексы (taggeditem.tag_id)
, (item.id)
, (item.rating
)
Проблема с этим запросом заключается в том, что mysql не может использовать индекс для item.rating, поскольку ключ, используемый для извлечения строк, не совпадает с тем, который используется в ORDER BY (MySQL: ORDER BY Optimization). Это приводит к использованию временной таблицы и файловой системы, что в свою очередь приводит к медленному времени выполнения.
Решение, с которым я столкнулся, - это денормализовать поле сортировки в таблице taggeditem
, чтобы я мог создать индекс (tag_id, item_rating)
на taggeditem
.
Я искал похожие вопросы в SO и нашел только этот: Mysql медленный запрос: INNER JOIN + ORDER BY вызывает filesort. Решение было таким же.
Итак, я хочу спросить, это общее решение этой проблемы? Является ли хорошей практикой денормализовать кучу полей сортировки для taggeditem, таких как созданный, рейтинг? В SO вы можете сортировать по 4 различным параметрам (новейшим, горячим, голосам, активным) - означает ли это, что они денормализовали поля, которые используются для сортировки результатов? Есть ли альтернативы этому решению?
Существует стандартная альтернатива - изменение системных переменных сервера. Например, вы можете поэкспериментировать с параметром sort_buffer_size (по умолчанию 2 МБ). Подробнее об этом.
Как только вы используете JOIN и отфильтровываете на объединенной таблице, вы застряли с плохой производительностью.
Как вы сказали, единственный способ избежать этого - создать денормализованную таблицу.
Для типов SO, я думаю, что у них нет такой проблемы: им просто нужно сортировать ответы по столбцу таблицы ответов (что-то вроде SELECT * FROM answers WHERE question_id = 1234 SORT BY answer_date
, с индексом на question_id, answer_date
)
Я также ищу такие решения с многозначными столбцами, и это действительно сложно (денормализованные данные будут огромными, так как нужно перекрещивать все значения в многозначных столбцах)