Инфраструктура сервиса: кэширование
Кэш - первый инструмент против медленной БД, и первый же источник тонких багов: устаревшие данные и 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, остальные разбираются в тренажёре.
- Почему кэшу задают TTL (время жизни ключа)?A)Чтобы Redis работал быстрее, обслуживая ключи с меньшим сроком жизниB)Чтобы ключи не занимали больше одного мегабайта каждыйC)Чтобы запретить читать ключ до истечения заданного времени жизниD)Чтобы устаревшие данные сами протухали и подтягивались заново
показать ответ и разбор
+D)Чтобы устаревшие данные сами протухали и подтягивались заново// разбор: Данные в кэше устаревают относительно источника; TTL ограничивает, сколько кэш может быть рассинхронизирован — по истечении ключ протухает и подтянется свежим. Это дешёвая инвалидация «по времени». Точечная инвалидация (сбросить ключ при изменении данных) точнее, но сложнее; часто их комбинируют.
- Популярный ключ истёк, и сотни запросов разом ломанулись за ним в БД. Как называется и как лечить?A)Утечка соединений с базой: лечится перезапуском пула соединенийB)Переполнение памяти Redis: лечится увеличением размера кэшаC)Cache miss: это нормально, лечения не требует и вреда не несётD)Cache stampede: лечат локом на пересчёт или ранним обновлением
показать ответ и разбор
+D)Cache stampede: лечат локом на пересчёт или ранним обновлением// разбор: Cache stampede (thundering herd): когда горячий ключ истекает, все ждавшие его запросы одновременно идут пересчитывать значение в БД и роняют её. Лечат блокировкой на пересчёт (один считает, прочие ждут результат), досрочным фоновым обновлением до истечения или джиттером TTL, чтобы ключи не протухали залпом.
- Что кеш (Redis) даёт для частых чтений?A)Обеспечивает, что данные в нём свежие и совпадают с базойB)Становится главным хранилищем данных вместо основной базыC)Ускоряет запись данных, принимая все изменения вместо базы данных приложенияD)Отдаёт горячие данные из памяти, снижая задержку и нагрузку на БД
показать ответ и разбор
+D)Отдаёт горячие данные из памяти, снижая задержку и нагрузку на БД// разбор: Кеш держит результат дорогого чтения в быстрой памяти и отдаёт его повторным запросам без обращения к БД — падает задержка и снижается нагрузка на базу. Источник правды при этом остаётся в БД: кеш лишь ускоряет повторные чтения и может отставать. Кешируют то, что дорого получить и что относительно редко меняется.
- Зачем ключам кеша задают TTL (время жизни)?A)Чтобы кеш меньше расходился с базой — при TTL данные держатся свежимиB)Чтобы зашифровать значение ключа на заданный промежуток времениC)Чтобы ускорить чтение: ключи с TTL Redis отдаёт быстрее, чем ключи без негоD)Ограничить, насколько кеш может разойтись с источником — потом обновится
показать ответ и разбор
+D)Ограничить, насколько кеш может разойтись с источником — потом обновится// разбор: TTL задаёт, сколько ключ живёт: по истечении он протухает и при следующем промахе подтянется свежим из источника. Это дешёвая инвалидация «по времени» — она ограничивает, насколько сильно кеш может разойтись с БД, и не даёт памяти бесконтрольно расти. Внутри окна TTL данные всё же могут быть слегка устаревшими; для критичной свежести добавляют инвалидацию по событию.
- Горячий ключ истёк, и сотни запросов разом бросились пересчитывать его в БД. Что это и как лечат?A)Сбой сети до Redis, решается переподключением клиентов к серверу кешаB)Переполнение памяти Redis, лечится увеличением лимита maxmemory на сервереC)Cache stampede: лок на пересчёт, раннее обновление, джиттер TTLD)Нормальная работа кеша: одновременный пересчёт горячего ключа базе не вредит
показать ответ и разбор
+C)Cache stampede: лок на пересчёт, раннее обновление, джиттер TTL// разбор: Cache stampede (thundering herd): популярный ключ протух, и множество запросов одновременно ловят промах и идут пересчитывать значение в БД, перегружая её. Средства: блокировка на пересчёт (считает один, остальные ждут результат), вероятностное раннее обновление до истечения, джиттер TTL (чтобы ключи не истекали синхронно) и отдача слегка устаревшего значения на время пересчёта.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.