В чем суть теоремы Брюера (CAP-теоремы) для распределенных систем?

«В чем суть теоремы Брюера (CAP-теоремы) для распределенных систем?» — вопрос из категории Архитектура, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

CAP-теорема (теорема Брюера) — это фундаментальный принцип, утверждающий, что в распределенной системе, подверженной сетевым разделениям (Partition, P), невозможно одновременно обеспечить все три свойства:

  1. Согласованность (Consistency, C): Все узлы видят одни и те же данные в один и тот же момент времени. Каждое чтение возвращает самую последнюю запись или ошибку.
  2. Доступность (Availability, A): Каждый запрос (чтение или запись) к работоспособному узлу получает не-ошибочный ответ в разумные сроки, без гарантии, что это самые свежие данные.
  3. Устойчивость к разделению (Partition tolerance, P): Система продолжает функционировать, несмотря на произвольный обрыв связи (потерю сообщений) между узлами.

Ключевой вывод: При возникновении сетевого раздела (P) система может гарантировать либо Согласованность (C), либо Доступность (A), но не оба свойства сразу.

Практические следствия и примеры систем:

Выбор при разделении Характеристика Примеры систем
CP (Consistency + Partition Tolerance) Жертвует доступностью. При разделении не отвечающие узлы блокируют запись/чтение до восстановления согласованности. HBase, MongoDB (с настройками strong consistency), ZooKeeper, традиционные RDBMS в кластере.
AP (Availability + Partition Tolerance) Жертвует согласованностью. Система остается доступной для чтения и записи, но данные на разных узлах могут временно расходиться (eventual consistency). Cassandra, DynamoDB, CouchDB, Riak.
CA (Consistency + Availability) Теоретически невозможна в распределенной системе. Реализуется только в системах без разделений (например, одиночный узел БД). Неприменимо к распределенным системам.

Пример AP-подхода (Cassandra):

// Запись в Cassandra (записывается на узел-координатор и реплики)
session.execute("INSERT INTO users (id, name) VALUES ('user1', 'Alice')");
// Немедленное чтение может вернуть устаревшие данные,
// если реплика, с которой идет чтение, еще не получила обновление.
// Это плата за доступность при разделении.
ResultSet rs = session.execute("SELECT name FROM users WHERE id = 'user1'");

Важно: CAP описывает крайние случаи. На практике многие системы допускают тонкую настройку (например, уровни согласованности в Cassandra) и стремятся к балансу, часто выбирая базовую доступность и итоговую согласованность (BASE) вместо строгой ACID.