Был ли опыт реализации кэширования на бэкенде?
Что проверяют
Был ли у вас реальный кэш в проде и понимаете ли вы его цену: устаревшие данные, инвалидацию, отказ Redis. Следом почти всегда спрашивают, как вы сбрасывали кэш при изменении данных и что происходило, когда популярный ключ истекал под нагрузкой.
Как отвечать
Стройте ответ на своём случае: что тормозило, что именно закэшировали, как измерили эффект. Технический каркас для Python-бэкенда:
- уровень: в процессе (
functools.lru_cache,cachetools.TTLCache), общий кэш в Redis черезredis-pyилиredis.asyncio, кэш фреймворка (django.core.cache,@cache_page), HTTP-кэш черезCache-ControlиETagна уровне CDN или nginx; - стратегия: чаще всего cache-aside. Читаем ключ, при промахе идём в базу и кладём результат с TTL. При записи ключ удаляют, а не перезаписывают, так меньше гонок;
- ключи: в ключ входят все параметры запроса и версия формата (
user:v2:42), тогда смена структуры данных не требует ручной чистки; - лавина при истечении: когда популярный ключ истекает, сотни запросов одновременно идут в базу. Помогают блокировка через
SET lock:key 1 NX EX 10, случайный разброс TTL и досрочное обновление; - метрики: доля попаданий, память Redis, число вытеснений. Без hit ratio нельзя сказать, работает ли кэш вообще.
Проценты ускорения называйте только свои и измеренные, например p95 до и после по метрикам. Если опыта не было, скажите прямо и расскажите, как подошли бы к задаче, начав с замера: какой запрос медленный и насколько данные терпят устаревание.
Где спотыкаются
Используют lru_cache в приложении под gunicorn с несколькими воркерами. У каждого процесса свой кэш, сброс в одном не доходит до других, и пользователи видят разные данные.
Вешают lru_cache на метод класса. Кэш держит ссылку на self, и объекты не освобождаются.
Не продумывают инвалидацию и на вопрос «как обновить данные» отвечают «подождать TTL».