Как оптимизировать выдачу топа популярных фотографий с использованием кэширования и индексации
Что проверяют
Умеете ли вы превратить тяжёлую сортировку по всей таблице на каждый запрос в дешёвое чтение готового результата. Следом обычно спрашивают, насколько свежим должен быть топ и что случится с базой в момент, когда кэш протух.
Как отвечать
Сначала уточните требования: по какому показателю топ (лайки, просмотры, их взвешенная сумма), за какой период и допустима ли задержка в минуту. От этого зависит всё остальное.
Дальше слои:
- формула популярности: для «горячего» топа используют затухание по времени, например счёт делится на возраст в часах в степени больше единицы, и старые фото уступают новым;
- индекс в базе: денормализованный счётчик в таблице фото и индекс по нему, частичный на опубликованные фото. Тогда
ORDER BY score DESC LIMIT 20читает двадцать записей индекса вместо сортировки всей таблицы. Топ за период так не ускорить, для него подходит материализованное представление сREFRESH MATERIALIZED VIEW CONCURRENTLYпо расписанию (ему нужен уникальный индекс); - Redis: отсортированное множество с ключом на каждые сутки, лайк увеличивает счёт командой
ZINCRBY, топ читаетсяZRANGE key 0 19 REVза O(log n + k). Суточные ключи с TTL склеиваются в недельный топ черезZUNIONSTORE; - кэш готового ответа: сериализованный топ в Redis или в кэше Django с TTL, плюс HTTP-кэш и CDN, если топ одинаков для всех.
Пересчёт удобно вынести в периодическую задачу (Celery beat), а счётчики обновлять пачками из очереди событий, а не отдельным UPDATE на каждый лайк.
Где спотыкаются
Не защищаются от одновременного промаха кэша. Когда ключ истекает под нагрузкой, сотни запросов разом идут в базу. Помогают блокировка на пересчёт, отдача устаревшего значения на время обновления и случайный разброс TTL.
Тащат Elasticsearch ради сортировки по одному числу. Это отдельный кластер и синхронизация данных там, где хватает индекса или отсортированного множества в Redis.
Сбрасывают кэш топа на каждый лайк. При активной аудитории он не живёт и секунды, пользы от него нет.