сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Docker и Kubernetes

Инфраструктура сервиса: кэширование

Кэширование: скорость и её ловушки

Кэш - первый инструмент против медленной БД, и первый же источник тонких багов: устаревшие данные и stampede, роняющий базу. Собес проверяет, понимаешь ли ты, что кэш не источник правды и как переживать истечение горячего ключа.

Типовые формулировки: «зачем TTL?», «что такое cache stampede и как лечить?», «что можно считать источником правды».

Кэш над БД и роль TTL

Кэш (обычно Redis) держит результат дорогого чтения в быстрой памяти и отдаёт его повторным запросам БЕЗ обращения к БД - падают и задержка, и нагрузка на базу. Но источник правды остаётся в БД: кэш это ускоряющий слой поверх, а не хранилище истины. При cache miss идут в источник и кладут результат в кэш.

TTL (time to live) ограничивает, насколько кэш может разойтись с источником: по истечении ключ протухает и подтянется свежим. Это дешёвая инвалидация «по времени» - не нужно ловить каждое изменение, достаточно смириться с рассинхроном на срок TTL. Кэшируют дорогое чтение, которое РЕДКО меняется; часто меняющееся без инвалидации по событию будет отдавать устаревшее.

// Считать кэш источником правды - ошибка: данные летучи и могут устареть, правду держит БД.

кэш
быстрый слой над БД для частых чтений
TTL
время жизни ключа: ограничивает рассинхрон с источником

Cache stampede и как его пережить

Cache stampede (thundering herd): горячий ключ истёк, и сотни запросов РАЗОМ обнаруживают miss и идут пересчитывать значение в БД одновременно, роняя её именно в пик. Парадокс кэша: чем популярнее ключ, тем больнее момент его истечения.

Лечат тремя приёмами. Лок на пересчёт: только один запрос считает, остальные ждут или отдают старое. Раннее обновление: обновить ключ до истечения. Джиттер TTL: разбросать сроки жизни, чтобы горячие ключи не истекали одновременно. Одинаковый TTL у пачки горячих ключей - прямой путь к синхронному stampede.

// Отсюда практика: не ставь просто фиксированный TTL горячим ключам - добавляй случайный разброс и защищай пересчёт локом.

cache stampede
лавина запросов в БД после истечения горячего ключа
инвалидация
сброс устаревших данных: по TTL или по событию

Как отвечать: «Что такое cache stampede и как его лечить?»

Это лавина запросов в БД в момент истечения горячего ключа. Пока ключ жив, все запросы обслуживаются из кэша, но как только он протух, сотни одновременных запросов разом видят промах и все вместе идут пересчитывать значение в базу, и роняют её ровно в пик нагрузки. Лечу тремя способами, обычно в комбинации. Лок на пересчёт: пересчитывает только один запрос, остальные ждут результат или получают старое значение. Раннее, фоновое обновление ключа до его истечения. И джиттер TTL - добавляю случайный разброс к времени жизни, чтобы горячие ключи не истекали синхронно. Фиксированный одинаковый TTL пачке горячих ключей как раз и провоцирует stampede.

Точно описан механизм (синхронный miss в пик), даны все три средства с объяснением каждого и назван антипаттерн одинакового TTL - практический разбор реальной аварии.

На чём валят

  • Считать кэш источником правды: данные летучи и могут устареть - правда в БД.
  • Одновременное истечение горячих ключей → stampede; спасают джиттер TTL и лок на пересчёт.
  • Кэшировать часто меняющееся без инвалидации по событию - отдаёшь устаревшее.
  • Забыть про cache miss путь: при промахе надо сходить в источник и положить результат в кэш.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.

  1. #caching_layers1 / 5
    Почему кэшу задают TTL (время жизни ключа)?
    A)Чтобы Redis работал быстрее, обслуживая ключи с меньшим сроком жизни
    B)Чтобы ключи не занимали больше одного мегабайта каждый
    C)Чтобы запретить читать ключ до истечения заданного времени жизни
    D)Чтобы устаревшие данные сами протухали и подтягивались заново
    показать ответ и разбор
    +D)Чтобы устаревшие данные сами протухали и подтягивались заново

    // разбор: Данные в кэше устаревают относительно источника; TTL ограничивает, сколько кэш может быть рассинхронизирован — по истечении ключ протухает и подтянется свежим. Это дешёвая инвалидация «по времени». Точечная инвалидация (сбросить ключ при изменении данных) точнее, но сложнее; часто их комбинируют.

  2. #caching_layers2 / 5
    Популярный ключ истёк, и сотни запросов разом ломанулись за ним в БД. Как называется и как лечить?
    A)Утечка соединений с базой: лечится перезапуском пула соединений
    B)Переполнение памяти Redis: лечится увеличением размера кэша
    C)Cache miss: это нормально, лечения не требует и вреда не несёт
    D)Cache stampede: лечат локом на пересчёт или ранним обновлением
    показать ответ и разбор
    +D)Cache stampede: лечат локом на пересчёт или ранним обновлением

    // разбор: Cache stampede (thundering herd): когда горячий ключ истекает, все ждавшие его запросы одновременно идут пересчитывать значение в БД и роняют её. Лечат блокировкой на пересчёт (один считает, прочие ждут результат), досрочным фоновым обновлением до истечения или джиттером TTL, чтобы ключи не протухали залпом.

  3. #caching_layers3 / 5
    Что кеш (Redis) даёт для частых чтений?
    A)Обеспечивает, что данные в нём свежие и совпадают с базой
    B)Становится главным хранилищем данных вместо основной базы
    C)Ускоряет запись данных, принимая все изменения вместо базы данных приложения
    D)Отдаёт горячие данные из памяти, снижая задержку и нагрузку на БД
    показать ответ и разбор
    +D)Отдаёт горячие данные из памяти, снижая задержку и нагрузку на БД

    // разбор: Кеш держит результат дорогого чтения в быстрой памяти и отдаёт его повторным запросам без обращения к БД — падает задержка и снижается нагрузка на базу. Источник правды при этом остаётся в БД: кеш лишь ускоряет повторные чтения и может отставать. Кешируют то, что дорого получить и что относительно редко меняется.

  4. #caching_layers4 / 5
    Зачем ключам кеша задают TTL (время жизни)?
    A)Чтобы кеш меньше расходился с базой — при TTL данные держатся свежими
    B)Чтобы зашифровать значение ключа на заданный промежуток времени
    C)Чтобы ускорить чтение: ключи с TTL Redis отдаёт быстрее, чем ключи без него
    D)Ограничить, насколько кеш может разойтись с источником — потом обновится
    показать ответ и разбор
    +D)Ограничить, насколько кеш может разойтись с источником — потом обновится

    // разбор: TTL задаёт, сколько ключ живёт: по истечении он протухает и при следующем промахе подтянется свежим из источника. Это дешёвая инвалидация «по времени» — она ограничивает, насколько сильно кеш может разойтись с БД, и не даёт памяти бесконтрольно расти. Внутри окна TTL данные всё же могут быть слегка устаревшими; для критичной свежести добавляют инвалидацию по событию.

  5. #caching_layers5 / 5
    Горячий ключ истёк, и сотни запросов разом бросились пересчитывать его в БД. Что это и как лечат?
    A)Сбой сети до Redis, решается переподключением клиентов к серверу кеша
    B)Переполнение памяти Redis, лечится увеличением лимита maxmemory на сервере
    C)Cache stampede: лок на пересчёт, раннее обновление, джиттер TTL
    D)Нормальная работа кеша: одновременный пересчёт горячего ключа базе не вредит
    показать ответ и разбор
    +C)Cache stampede: лок на пересчёт, раннее обновление, джиттер TTL

    // разбор: Cache stampede (thundering herd): популярный ключ протух, и множество запросов одновременно ловят промах и идут пересчитывать значение в БД, перегружая её. Средства: блокировка на пересчёт (считает один, остальные ждут результат), вероятностное раннее обновление до истечения, джиттер TTL (чтобы ключи не истекали синхронно) и отдача слегка устаревшего значения на время пересчёта.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.