Разбираем теорему CAP как ментальную модель: что означают C, A и P, почему разделения сети неизбежны и как выбирать компромиссы в системах.

Теорема CAP часто звучит как абстрактная «математика про три буквы», но на практике это удобная ментальная модель: она помогает заранее решить, что именно система должна делать при сбоях связи, а не спорить постфактум, почему «вдруг всё сломалось».
Эрик Брюэр — исследователь и практик распределённых систем, который в конце 1990‑х сформулировал идею, позже известную как теорема CAP. С тех пор инфраструктура стала быстрее и сложнее, но ключевая проблема не исчезла: сеть может подвести, а данные могут оказаться в нескольких местах одновременно.
CAP продолжают обсуждать, потому что она называет неприятную правду вслух: некоторые требования «хочу всё сразу» несовместимы в момент сетевого разделения.
Важно: CAP — не рейтинг «хороших» и «плохих» баз данных и не повод гордиться тем, что ваша система «выбрала правильную букву». Это язык для обсуждения компромиссов:
По сути, CAP помогает перевести разговор из «нам нужна надёжность» в конкретику:
Представьте сервис с двумя дата-центрами. Пользователь меняет адрес доставки в одном, а через минуту оформляет заказ через другой, но связь между центрами пропала. В этот момент системе нужно выбрать: отказать в операции ради согласованности или принять операцию, рискуя расхождением данных.
CAP полезна тем, что заставляет принять это решение заранее — на уровне продукта и архитектуры. Это особенно важно, когда вы быстро собираете MVP или внутренний сервис: в TakProsto.AI удобно накидать прототип (web на React, backend на Go + PostgreSQL) и сразу зафиксировать в «planning mode», какие операции у вас CP, а какие можно оставить AP — до того, как появятся реальные пользователи и инциденты.
Теорема CAP оперирует тремя свойствами распределённой системы. Важно: это не «про качество продукта вообще», а про поведение хранилища/сервиса данных в момент, когда часть узлов перестаёт видеть часть других узлов.
Consistency в CAP — это не «данные всегда логически верные» и не «бизнес-правила соблюдены». Речь про наблюдаемую согласованность между копиями данных.
Простая проверка: если клиент записал значение, а потом (или другой клиент) читает эти данные, увидит ли он самую свежую запись, как будто система одна-единственная?
Если ответ «да» — система ближе к C. Если иногда читается «вчерашняя версия» — C ослаблена (и это может быть осознанным выбором).
Availability в CAP — не то же самое, что аптайм 99.9% по мониторингу. В CAP это свойство звучит так: каждый запрос к работающему узлу получает ответ (не обязательно «успешный» по смыслу бизнеса), и ответ приходит за разумное время.
Ключевой момент: если система в проблемном режиме начинает «подвисать» или часто возвращать таймауты, это уже удар по A, даже если формально «серверы живы».
Partition tolerance — способность продолжать работу, когда сеть «режется» на части: узлы недоступны друг другу, пакеты теряются, растут задержки. Это может быть не авария дата-центра, а обычные сетевые сбои, перегрузка, проблемы маршрутизации.
В условиях разделения система должна решить, что важнее:
Термины совпадают с «обычными» словами, но означают более узкие вещи:
В теореме CAP буква P (Partition tolerance) иногда воспринимается как «ещё одна характеристика, которую можно улучшать». На практике для распределённых систем P — не настройка, а факт реальности: сеть периодически ведёт себя так, будто часть узлов не может связаться с другой частью.
«Разделение» (partition) появляется не только при физическом обрыве. Его провоцируют и более «мягкие» ситуации:
С точки зрения приложения итог один: часть запросов не может надёжно подтвердить, что другая сторона получила или обработала данные.
Если проектировать систему так, будто сеть всегда стабильна, ошибки будут не «редкими исключениями», а источником систематических инцидентов: зависания, лавина ретраев, рост очередей, «плавающие» баги, которые сложно воспроизвести. Чем больше компонентов и чем дальше они друг от друга, тем выше шанс попасть в ситуацию неопределённости.
Когда сервер упал, картина относительно ясная: он не отвечает никому. При partition всё коварнее: сервер может быть жив, обслуживать локальных клиентов и писать данные — просто другая часть системы его «не видит». В итоге возникают два «островка истины», которые расходятся во времени и состояниях.
Если у вас более одного узла и между ними сеть, то P неизбежно. Поэтому главный вопрос не «как избежать partition», а как система должна себя вести, когда оно случилось: продолжать отвечать ценой расхождения данных или останавливать часть операций ради строгой согласованности.
Классическая формулировка теоремы CAP звучит так: если сеть разделилась (partition), то система не может одновременно гарантировать и согласованность (C), и доступность (A).
Важно слово «гарантировать»: речь не о «обычно работает», а о том, что система обязана делать в худший момент — когда часть узлов не может поговорить с другой частью.
При сетевом разделении у вас остаётся два принципиальных варианта поведения:
«P» почти всегда считаем неизбежным свойством распределённой системы, поэтому на практике CAP чаще читают как: при разделении выбираем между C и A.
В CP-подходе система предпочитает не отвечать, чем ответить противоречиво.
Пользовательский опыт обычно выглядит так:
Проще: лучше «временно недоступно», чем «доступно, но неправда».
В AP-подходе система старается всегда ответить, даже если разные части кластера видят мир по‑разному.
Для пользователя это может означать:
Проще: лучше «работает сейчас», но возможны расхождения, которые потом сойдутся.
CAP не требует выбрать один режим навсегда для всей системы. Реальные продукты часто принимают решение по операциям и по частям данных: например, платежи делать CP (строгая корректность важнее), а ленту обновлений — AP (важнее отклик и непрерывная работа).
Теорема лишь фиксирует границу: когда сеть разделилась, одновременно «всегда правильно» и «всегда доступно» не получится.
CAP часто обсуждают как выбор «внутри системы», но на самом деле он проявляется в том, что видит и переживает пользователь в момент проблем со связью между узлами.
В CP-подходе при сетевом разделении система старается не выдавать ответ, который может противоречить «истине». Поэтому пользователь чаще сталкивается с таймаутами, ошибками «сервис недоступен», повторной отправкой запроса, иногда — с временной блокировкой операций.
С точки зрения ощущений это выглядит как «не получилось, попробуйте позже», зато когда получилось — результат ожидаемо корректный.
Так обычно делают там, где цена ошибки выше цены ожидания:
Иными словами, система предпочитает честно отказать, чем показать потенциально «старые» данные или принять действие, которое потом придётся откатывать.
В AP-подходе сервис продолжает отвечать даже при проблемах связи между частями системы. Пользователь реже видит ошибки, интерфейс остаётся «живым», действия принимаются и подтверждаются.
Обратная сторона — временные расхождения. Пользователь может увидеть:
Это хорошо подходит для сценариев, где важнее непрерывность, а не мгновенная точность: ленты и рекомендации, просмотры, телеметрия, аналитика, кэшированные карточки контента.
Практический вопрос не «CP или AP вообще», а «для каких операций и данных». Часто один продукт комбинирует подходы: платежи и права — CP, а лента и счётчики — AP.
Полезная проверка границы предметной области: если пользователь может принять неверное решение из-за временной неточности (купить не то, получить лишний доступ, увидеть неправильный баланс) — тяготеем к CP. Если же неточность лишь слегка ухудшает опыт и сама «рассосётся» при синхронизации — AP обычно оправдан.
Многие читают теорему CAP так, будто «согласованность» — это кнопка: либо данные всегда одинаковые везде, либо «хаос». В реальных системах согласованность почти всегда выбирают как диапазон приемлемого расхождения и правила, как с ним жить.
Представьте две реплики.
Вы записали новое значение в реплику A (например, изменили адрес доставки). Сразу после этого запрос на чтение попадает в реплику B — и там ещё старый адрес. Это не обязательно ошибка: это проявление задержки распространения изменений.
Ещё один сценарий — конфликт обновлений: два клиента почти одновременно меняют один и тот же объект в разных репликах (например, редактируют профиль или корзину). В какой-то момент система должна решить, что считать «истиной».
Удобно думать о согласованности как о шкале:
Когда возможны конфликты, заранее выбирают стратегию:
Главное — формулировать требования измеримо: «устаревание не более X секунд», «выплата без двойного списания», «две правки не должны теряться». Так согласованность становится управляемым параметром, а не верой в идеальную синхронность.
Репликация нужна, чтобы сервис не падал из‑за одной машины и чтобы данные были ближе к пользователю. Но как только у вас несколько копий одних и тех же данных, появляется вопрос: сколько копий должны подтвердить операцию, чтобы мы считали её успешной? Здесь и прячется компромисс между согласованностью (C) и доступностью (A).
Кворум — это «большинство реплик». Если данные лежат на трёх узлах, кворум обычно равен двум: операция считается выполненной, когда её подтвердили 2 из 3.
Интуиция простая: если чтение и запись опираются на пересекающиеся множества реплик, то шанс увидеть «старое» состояние заметно падает.
Во многих хранилищах можно выбрать параметры вида:
Упрощённое правило: если R + W > N, чтения и записи пересекаются, и система чаще ведёт себя «как согласованная». Если сделать W меньше, записи будут проходить при меньшем числе доступных узлов — это повышает доступность, но увеличивает риск прочитать устаревшие данные.
Важно: это не магическая гарантия, а настройка «в какую сторону» вы смещаете поведение при сбоях и задержках.
Кворум — это плата за уверенность:
То есть кворум помогает приблизиться к C, но делает систему более чувствительной к сетевым проблемам и «тормозящим» узлам.
Полезно заранее разделить операции на два класса:
Так вы обсуждаете не абстрактные C и A, а конкретный пользовательский риск: где лучше «подождать и быть точным», а где — «ответить быстро, даже если данные слегка отстают».
Когда обсуждают теорему CAP, часто путают «разделение сети» (P) с обычной медлительностью. На практике всё начинается с задержек: запрос не обязательно «не дошёл», он может просто идти слишком долго. И именно здесь рождается ощущение, что система упала.
Задержка (latency) — это время, которое проходит от отправки запроса до получения ответа. В распределённых системах она складывается из сети, очередей, диска, CPU, пауз GC, перегруженных балансировщиков и т. д.
CAP включается в момент, когда задержка становится настолько большой, что для клиентов и сервисов это уже не «медленно», а «без ответа». Тогда участники системы начинают вести себя так, будто между ними произошло сетевое разделение.
Таймаут — это ваше явное решение, сколько ждать. Если ответ не пришёл вовремя, клиент считает операцию неуспешной и запускает повторы, переключение на реплику, фолбэк или возвращает ошибку пользователю.
Ключевой момент: даже если сервер всё ещё обрабатывает запрос и позже запишет данные, клиент уже ушёл. Так появляются «двойные списания», повторные заказы, расхождения между сервисами — и необходимость выбирать, что важнее: согласованность (C) или доступность (A) в условиях «как будто P».
Представьте, что вы держите данные в двух регионах. Обычно межрегиональная задержка 60–80 мс, но из‑за перегрузки канала стала 300–500 мс с редкими всплесками до секунд.
Средняя задержка почти всегда обманчива. Смотрите tail latency: p95/p99 (а иногда p99.9). Именно хвосты обычно «съедают» таймауты и создают эффект «всё легло».
Настройка таймаутов — это часть архитектурного выбора: таймауты должны соответствовать SLO, реальному p99 и цене ошибки. И обязательно тестируйте повторы и деградацию — иначе вы сами создадите себе «разделение сети» лавиной ретраев.
CAP — не про «правильный выбор», а про осознанные требования. Чтобы не спорить абстрактно («нам нужна согласованность!»), зафиксируйте, что именно система обязана делать при сбоях сети и задержках.
Сформулируйте 2–5 утверждений, которые нельзя нарушать никогда — даже ценой отказа части запросов. Примеры:
Если инварианты жёсткие, вы заранее допускаете, что при разделении сети часть операций будет отклоняться или ставиться «на паузу» ради C.
Заранее решите, как выглядит сервис в режиме AP (или «мягкой» согласованности):
Главное — чтобы деградация была понятна пользователю и не ломала инварианты из пункта 1.
Разделите операции по риску и поведению при повторах:
Это помогает точечно выбирать компромисс: например, «каталог товаров — AP, оформление заказа — CP».
Без измерений компромисс превращается в веру. Зафиксируйте:
Когда эти пункты записаны, архитектурное решение перестаёт быть спором про термины и становится проверяемой спецификацией поведения системы при сбоях.
CAP полезна ровно до тех пор, пока вы обсуждаете реальные сбои и поведение системы. Большинство проблем начинаются, когда теорему превращают в «ярлык для архитектуры» или в обещание маркетинга.
Добавление серверов помогает выдерживать нагрузку и переживать падение отдельных узлов, но не отменяет сетевые разделения (P). Если сеть между двумя частями кластера рвётся, системе всё равно нужно решить: останавливаем часть операций ради согласованности (C) или продолжаем отвечать, рискуя расхождением (A).
Как избежать: заранее определить, какие операции обязаны быть согласованными, а какие могут «догонять» позже. И зафиксировать, что при разделении сеть диктует выбор, а не количество машин.
ACID — про поведение транзакций в пределах одной базы (и её журнала/блокировок). CAP — про ситуацию, когда данные распределены, и связь между частями системы может пропасть или задержаться.
Как избежать: в разговорах уточняйте контекст: «Мы обсуждаем транзакцию внутри одного узла?» или «Мы обсуждаем запись, которая должна разойтись по репликам через сеть?» Это разные задачи и разные компромиссы.
Доступность в CAP — это не про месячный аптайм. Это про то, что каждый запрос получает ответ (пусть даже ошибку бизнес-уровня) во время проблем со связью между узлами.
Как избежать: измеряйте A сценарно: «что вернёт система при разделении?» и «какие запросы начнут таймаутиться?» — а не только общими процентами.
Самый практичный способ — список конкретных ситуаций и ожидаемых ответов:
Фиксируйте решения как требования к продукту и проверяйте их тестами отказов и метриками (подробнее — в разделе /blog/observability-basics).
CAP полезен только тогда, когда превращается в конкретные решения: что именно делает система, когда сеть «режется», узлы тормозят или часть реплик недоступна.
Самый практичный артефакт — короткий документ (1–2 страницы), где описано: какой выбор делаем при разделении сети и как это проявляется для пользователя.
Укажите явно:
Если вы разрабатываете продукт итеративно, полезно хранить такие договорённости рядом с исходниками и деплоем. Например, в TakProsto.AI удобно вести архитектурные заметки в planning mode, быстро поднимать стенды, а при изменении кворумов/таймаутов — откатываться через snapshots и rollback, если эксперимент ухудшил p99 или доступность.
В тестовом окружении регулярно моделируйте три класса событий:
Проверяйте не только «сервис жив», но и инварианты: нет ли двойных списаний, нарушений лимитов, некорректных статусов заказов.
Настройте алерты на ранние признаки деградации:
Важно заранее описать, какие метрики означают «мы в режиме CP» или «мы в режиме AP», чтобы дежурный понимал ожидаемое поведение.
Если хочется глубже в практику согласованности (кворумы, версии, разрешение конфликтов), переходите к материалу /blog/consistency-patterns.
А при выборе управляемой инфраструктуры и компромисса по SLA/цене полезно свериться с /pricing.
Лучший способ понять возможности ТакПросто — попробовать самому.