Проблема (почему Cache‑Aside не всегда безопасен)
В типичной схеме Cache‑Aside вы делаете:
UPDATE в БД.DELETE ключа в Redis (или обновление).Между этими операциями (в микросекунды) другой поток может прочитать кэш, в котором лежит старое значение, и вернуть его пользователю, пока БД уже обновлена. Это состояние гонки называется «гонка инвалидации кэша» (cache invalidation race). Она редка, но в высоконагруженных системах случается и приводит к неконсистентным данным. Попытки исправить синхронными блокировками (вариант C) или распределёнными транзакциями (вариант A) убивают производительность и усложняют код.
Почему CDC — лучшее решение
Change Data Capture (CDC) читает журнал транзакций вашей СУБД (WAL в PostgreSQL, binlog в MySQL) и превращает каждое изменение в событие. Это событие содержит старую и новую версию записи.
Паттерн с CDC:
Приложение пишет только в БД — никаких вызовов к Redis.
CDC-коннектор (например, Debezium) транслирует изменения в топик Kafka (или непосредственно в поток).
Отдельный сервис-консьюмер слушает этот топик и обновляет Redis: либо удаляет старый ключ, либо перезаписывает новым значением.
Почему это решает проблему гонки:
Кэш обновляется асинхронно после записи в БД. Между ними нет синхронного окна, где другой запрос мог бы прочитать старый кэш.
События приходят в том же порядке, что и изменения (благодаря порядку в WAL).
Даже если консьюмер немного отстаёт, eventual consistency допустима для большинства сценариев (например, просмотр каталога товаров). Если нужна строгая согласованность, можно настроить синхронный режим чтения из БД при сомнении.
Реальный пример из практики:
В сервисе рекомендаций Netflix используется CDC для синхронизации PostgreSQL и Elasticsearch. При обновлении данных о фильме изменение попадает в Kafka через Debezium, а затем индексатор обновляет Elasticsearch. Это позволяет выдерживать миллионы запросов без блокировок.
Что должен зафиксировать аналитик:
«Для поддержания актуальности кэша использовать асинхронную синхронизацию через CDC».
«Допустимая задержка обновления кэша — не более 100 мс».
«При недоступности консьюмера кэш может быть временно неконсистентным, но это допустимо».
Почему не подходят другие варианты:
A (синхронное обновление в той же транзакции) — требует распределённой транзакции (2PC), что медленно и не масштабируется. К тому же в Redis нет транзакций с БД.
C (блокировка записи в кэш на время обновления) — создаёт узкое место, замедляет запись, не решает проблему чтения во время блокировки.
D (всегда читать из БД) — отказ от кэша, что убивает производительность.
Вывод: CDC — это золотой стандарт для поддержания консистентности между разнородными хранилищами в распределённых системах. Аналитик, включающий этот паттерн в требования, помогает команде избежать сложных и медленных синхронных решений.