Задача «выбрать 10 случайных постов» часто решается так:
SELECT * FROM post ORDER BY RAND() LIMIT 10;
Для совсем небольших проектов это будет работать, но когда проект начнёт расти начнутся проблемы. И начнутся они уже с 1500 записей. На моей рабочей машине этот запрос выполняется где-то 400мс.
EXPLAIN
показывает нам Using temporary; Using filesort
, что означает создание временной таблицы (а для большого количества записей она ещё и на диск пишется) и не использование индекса. Виновник, как вы уже догадались — ORDER BY RAND()
.
Чтобы использовать индекс можно выбрать не все данные, а только id
:
SELECT id FROM post ORDER BY RAND() LIMIT 10
Теперь имея 10 id
получим посты:
SELECT post.* FROM ( SELECT id FROM post ORDER BY RAND() LIMIT 10 ) AS ids JOIN post ON post.id = ids.id
Получаем результат примерно за 10мс, что несомненно лучше. Но и этого может не хватить.
В этом случае можно поступить следующим образом:
- По крону получить 10—20 вариантов случайных записей.
- Записать в отдельную таблицу, предварительно очистив её.
Далее делаем rand(1, 20)
на сервере и выполняем очень простой и быстрый запрос:
SELECT * FROM random_post WHERE random_id = :random_id
Если нужно ещё быстрее — делаем аналог на Redis или memcached.