惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
博客园 - 【当耐特】
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Announcements
Recent Announcements
D
Docker
GbyAI
GbyAI
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
The Cloudflare Blog
雷峰网
雷峰网
A
About on SuperTechFans
小众软件
小众软件
博客园 - Franky
博客园 - 聂微东
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
C
Check Point Blog
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
V2EX
Engineering at Meta
Engineering at Meta
宝玉的分享
宝玉的分享
aimingoo的专栏
aimingoo的专栏
量子位
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
罗磊的独立博客
Martin Fowler
Martin Fowler
D
DataBreaches.Net
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Secure Thoughts
Project Zero
Project Zero
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tailwind CSS Blog
S
Schneier on Security
Blog — PlanetScale
Blog — PlanetScale
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
Security Latest
Security Latest
NISL@THU
NISL@THU
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Cache is hard — почему инвалидация кэша — это проблема согласованности, а не производительности
Andrey_Biryu · 2026-05-26 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение10 мин

Охват и читатели4K

Обзор

Привет, Хабр! Меня зовут Андрей Бирюков. Я эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу книги. В сегодняшней статье мы поговорим об инвалидации кеша и способах решения связанных с этим проблем.

В современном мире к скорости работы приложений предъявляются достаточно высокие требования. Конечно, можно пытаться выжать максимальную производительность из железа и до бесконечности оптимизировать алгоритмы. Но еще одним способом существенно увеличить производительность систем является кеширование. Конечно, удобно, когда у нас есть возможность сохранять результаты выполненных операций для увеличения производительности.

Но зачастую кэширование легко начать, но почти невозможно правильно закончить. И классическая проблема инвалидации кэша — это не поиск эффективного алгоритма сброса данных, а управление распределённой согласованностью в условиях асинхронности, гонок и частичных отказов. Об этом мы и будем говорить в сегодняшней статье.

Великое заблуждение о кэше

Кеш можно назвать молотком, который есть у каждого архитектора, и в любой непонятной ситуации он может им воспользоваться. Например, база данных отвечает медленно, а пользователь не хочет ждать, значит нам нужно хранить копию данных где‑то поближе. В оперативной памяти сервера, в Redis, на диске. Данные меняются редко, а читаются часто. Действительно кажется, что это идеальный сценарий. Вы ставите кеш, поднимаете время ответа с двухсот миллисекунд до пяти. И все вроде бы довольны.

Только потом кто‑то обновляет данные, и начинается ад. Наш основной инструмент — старый добрый TTL (время жизни записи в кэше) — работает, только если вам можно отдавать устаревшие данные в течение нескольких секунд или минут. Для табло курса валют это нормально, а вот для списка товаров с ограниченным остатком уже нет. Вы представляете, что пользователь видит «в наличии 3 штуки», оформляет заказ, а через секунду получает отказ, потому что товар уже купил другой человек? Получается, что кэш солгал ему.

Инвалидация по событию означает, что при обновлении базы мы отправляем команду «удалить ключ из кэша». Вроде бы звучит правильно. Но на практике оказывается, что событие идёт по сети, и, пока оно дошло, другой запрос мог уже прочитать старое значение из базы и записать его обратно в кэш. Получаем состояние гонки. Или кэш на секунду упал, событие потерялось, и теперь в кэше навсегда поселилась старая версия. Либо инвалидация произошла, но перед этим кто‑то успел сделать запрос и положил в кэш устаревшие данные — и они будут жить до следующей инвалидации, которой уже не будет.

Парадокс кэширования формулируется просто: кэш делает систему быстрее ценой нарушения согласованности. Вопрос не в том, как сделать инвалидацию идеальной. Вопрос в том, какую степень несогласованности вы готовы принять и как сделать её предсказуемой.

Библиотечная проблема

Чтобы понять всю глубину проблемы, рассмотрим классический сценарий, который можно называть «библиотечной проблемой». Представьте онлайн‑библиотеку, в которой пользователь А смотрит карточку книги — кэш отдаёт «в наличии 1 экземпляр». Пользователь Б в тот же момент берёт эту книгу — выполняется запрос в базу, остаток уменьшается до нуля. Система отправляет событие инвалидации кэша. Но оно ещё не дошло до кэша, а пользователь А уже оформляет заказ, читая из кэша устаревшую единицу. Через секунду он получит отказ, потому что настоящий остаток ноль.

Пользователь А будет зол, так как он не поймёт, почему система показала доступность, а потом отменила заказ. С его точки зрения, система неработоспособна.

А в чём, собственно, проблема? Система не нарушила никаких законов физики: данные в базе согласованы, кэш обновится через мгновение. Но для пользователя А была продемонстрирована несогласованность, так как был момент времени, когда два пользователя видели разное состояние одного и того же объекта. Один видел единицу, другой — ноль. Оба были правы с точки зрения своих источников, но одновременно существовать эти две правды не могли.

Это и есть фундаментальное ограничение. В распределённой системе без глобальной блокировки вы не можете гарантировать, что все клиенты видят одно и то же состояние в один и тот же момент времени без серьёзной платы за производительность. Кэш просто делает этот разрыв заметным и болезненным.

Выход из библиотечной проблемы — не лучшая инвалидация, а перепроектирование поведения. Например, не показывать точный остаток, а показывать статус «мало» или «проверяем доступность». Или при оформлении заказа не полагаться на кэш, а делать реальную резервацию товара в базе с блокировкой строки. Или внедрить «корзину ожидания»: пользователь выражает намерение купить, система резервирует товар на несколько минут, и только после этого показывает его другим как недоступный. Кэш продолжает использоваться для каталога — он всё ещё ускоряет пролистывание списков. Но для критической операции — остатка и резервации — кэш вообще не используется.

Это и есть главная мудрость: некоторые данные не должны попадать в кэш с возможностью несогласованности.

Паттерн «lease»

Итак, мы пришли к ситуации, когда, с одной стороны, мы не можем обойтись без кеша, но при этом несогласованность для нас тоже недопустима. В таком случае нам на помощь приходят блокировки. Здесь не идет речь о полных блокировках всей базы, а о достаточно умном блокировании, позволяющем избежать гонок. Один из таких механизмов — паттерн «lease» (аренда).

Когда клиент хочет прочитать данные из кэша и готов полностью за них отвечать, он не просто читает значение. Он запрашивает у «владельца кэша» (отдельного сервиса или Redis с Lua‑скриптами) короткоживущий токен с правом обновления. За время жизни токена (например, 5 секунд) никакой другой клиент не может изменить этот ключ. Если за это время приходит событие о реальном обновлении данных в базе, оно ставится в очередь и ждёт, пока истечёт токен. Клиент, владеющий токеном, может обновить кэш, и только после этого токен освобождается.

Как видно, здесь всё достаточно просто, но вернёмся к библиотеке. Снова наш пользователь А открывает карточку книги. Система выдаёт ему lease на 5 секунд. В течение этих пяти секунд все запросы остатка этой книги от других пользователей идут не в кэш, а напрямую в базу или ждут. Пользователь Б оформляет заказ — система видит активный lease и не трогает кэш, а работает с базой напрямую. Когда lease истекает (или пользователь А закрыл карточку), кэш может быть обновлён. Пользователь А за всё время держал в руках консистентное значение. Он не видел момента, когда единица превратилась в ноль.

Цена этого подхода — определённая сложность и задержки во время действия lease. Другие пользователи могут ждать освобождения кэша. Но в сценариях, где данные меняются нечасто и важна согласованность для каждого чтения (например, в биллинговых системах, при бронировании ресурсов), lease оправдан. Главный недостаток — lease требует централизованного координатора, который помнит о выданных токенах. Если этот координатор упал, система либо останавливается, либо теряет гарантии и возвращается к обычным гонкам.

Кеш как реплика

Современный подход к серьёзному кэшированию, который набирает популярность в высоконагруженных системах, — это рассматривать кэш не как временное хранилище, а как реплику базы данных для чтения. Идея заключается в следующем: вы отказываетесь от инвалидации в обычном понимании. Вместо этого приложение пишет в базу (например, PostgreSQL).

Отдельный процесс — Change Data Capture (CDC) — читает журнал транзакций базы (WAL, binlog) и транслирует каждое изменение в виде события в поток (Apache Kafka, RabbitMQ). Все кэши — их может быть несколько, в разных регионах — подписываются на поток и обновляют свои копии. Задержка между записью в базу и обновлением кэша — порядка десятков миллисекунд, предсказуема и конечна.

Преимущество этого подхода в том, что гонки «чтение старого значения + запись в кэш» просто не возникают. Потому что кэш никогда не читает данные из базы напрямую. Он только слушает события и обновляется. Если кэш упал и пропустил несколько событий, он при старте может подтянуть снапшот из базы и затем доесть события из Kafka с нужного смещения.

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

Рассмотрим небольшой пример: крупный сервис бронирования авиабилетов использует именно такую модель. База броней — PostgreSQL, CDC через Debezium в Kafka. Всего несколько десятков кэшей Redis по всему миру слушают изменения и показывают актуальные цены и остатки мест. Задержка от момента бронирования до обновления кэша во всех регионах — в среднем 120 миллисекунд, что для туриста незаметно. Но при этом система способна обрабатывать пики до 50 000 бронирований в секунду, а каждый билет виден консистентно во всех регионах с разницей в сотни миллисекунд.

Пламя, которое сожгло магазин

А теперь давайте разберём реальную историю, произошедшую в одном ритейлере. Компания имела классическую архитектуру: веб‑приложение, за ним — кэш Redis, за кэшем — PostgreSQL. Остатки товаров хранились в Redis с TTL 30 секунд и обновлялись из базы при промахе кэша. Инвалидация по событию вообще не использовалась — надеялись, что 30 секунд — допустимый люфт.

Так работало два года, но потом случилась распродажа на товар‑хит — игровую консоль. В течение двух минут пришло 5000 запросов. Представим, что на складе было 2000 единиц. Первые 2000 запросов прошли: Redis честно показывал остаток 2000, забирали. База тоже честно уменьшала остаток. Но Redis каждые 30 секунд перезагружал значение из базы.

Проблема возникла из‑за того, что Redis не блокировался во время обновления. Запрос номер 2001 пришёл в тот момент, когда Redis как раз перезагружал значение из базы — а база уже показывала 0. Но из‑за асинхронности Redis взял базы 0, записал в себя и с этого момента показывал всем «0». Всё правильно.

А вот запросы 2002–5000 получили 0 и ушли с пустыми руками. Но проблема была не в них. Проблема была в том, что пользователи, оформившие заказ в первые секунды, получили подтверждение, а через несколько минут — отмену. Почему? Потому что когда приложение получало запрос на покупку, оно сначала проверяло Redis. Если Redis говорил «есть остаток», приложение делало реальную резервацию в базе. В базе остаток был, блокировка проходила, заказ сохранялся. Но затем, через секунду, проверка антифрода отбраковывала заказ, потому что «несоответствие остатка» — какой‑то баг в интеграции.

В итоге честные покупатели получали подтверждение заказа и через 20 минут — письмо «к сожалению, товар закончился». В результате компания потеряла репутацию.

Урок этой истории не в том, что TTL плох. Урок в том, что кэш с TTL создаёт иллюзию актуальности. Если вы не можете жить с тем, что данные в кэше могут быть устаревшими на величину TTL, то данная технология вам не подходит. Переходите к CDC или используйте кэш только для данных, которые не меняются или меняются предсказуемо по расписанию. Никогда не кэшируйте складские остатки в высоконагруженной торговле — это прямой путь к катастрофе.

Кэшировать или не кэшировать: навигатор принятия решений

В завершении предлагаем не универсальную формулу, а набор вопросов, которые архитектор должен задать себе перед тем, как добавить кэш.

  • Первый вопрос. Что произойдёт, если пользователь увидит устаревшие данные? Если ничего страшного — например, аватарка пользователя загрузится на секунду старой — кэшируйте смело с любым TTL хоть в минуту. Если произойдёт финансовый ущерб, потеря заказа, юридические последствия — не кэшируйте эту сущность вообще, либо используйте только синхронную строгую согласованность с блокировками.

  • Второй вопрос. Как часто данные меняются относительно того, как часто они читаются? Классическая формула: кэш эффективен при соотношении чтений к записям больше 10 к 1. Если данные меняются чаще — кэш будет постоянно инвалидироваться и создавать нагрузку без пользы.

  • Третий вопрос. Готовы ли вы к распределённой отладке? Когда в системе есть кэш, база и, возможно, CDC, исчезает простая причинность. Вы не можете сказать «я обновил запись, теперь она везде такая». Вы можете сказать только «через некоторое время, если всё пойдёт нормально, она обновится везде». Если ваша команда не готова к такому уровню неопределённости и инструментам трассировки (распределённые трейсы, дашборды задержек), возможно, кэш добавит больше проблем, чем решений.

  • Четвёртый вопрос. Можете ли вы решить проблему без кэша? Часто оказывается, что медленная база данных — это симптом неправильных индексов, неоптимальных запросов или плохой схемы. Ускорение может принести вертикальное масштабирование (больше памяти, NVMe‑диски), использование read replica или изменение способа работы с данными. Кэш — это мощное, но опасное средство. Применяйте его, когда все более простые способы уже испробованы.

Делаем выводы

Кэширование — это всегда компромисс между скоростью и правдой. TTL даёт вам контролируемую ложь на заданный интервал. Инвалидация по событию пытается быть честной, но сталкивается с гонками и потерянными событиями. CDC и lease решают проблемы ценной сложности и координации.

Самый опасный вид кэширования — тот, который создаёт иллюзию актуальности, будучи по сути асинхронным. Вы не можете иметь быстрый распределённый кэш и строгую согласованность одновременно. Кэш не нарушает CAP‑теорему — он платит за скорость, жертвуя согласованностью. И как архитектор вы должны осознанно выбирать эту жертву, понимая её цену для бизнеса и пользователей.

Инвалидация кэша — действительно одна из наиболее сложных задач в компьютерных технологиях. Но если воспринимать её не как техническую задачу «как сбросить ключ», а как бизнес‑задачу «какой объём несогласованности допустим», решение становится ближе. Ответ часто лежит не в технологии, а в том, чтобы не кэшировать критичные данные вовсе, переложив ответственность за скорость на базу данных и её реплики.

Если тема кэширования, асинхронности и согласованности в распределённых системах для вас не абстрактная теория, а ежедневная боль в проде — присмотритесь к открытым урокам OTUS. Это бесплатные занятия в рамках онлайн-курсов: их проводят преподаватели-практики, можно познакомиться с экспертами, оценить формат обучения и задать вопросы по своим кейсам.

Ближайшие уроки по теме:

Приходите на открытые уроки, если хотите глубже разобраться, как проектировать системы, которые не просто быстро отвечают, но и сохраняют предсказуемость в реальных условиях нагрузки.

И подписывайтесь на канал OTUS в MAX — там делимся анонсами открытых уроков, полезными материалами и новостями для IT-специалистов.