Когда данные лежат на нескольких узлах, появляется вопрос: что увидит клиент, если узлы расходятся или связь между ними пропала.
CAP-теорема
В распределённой системе при сетевом разделении (Partition) нельзя одновременно гарантировать:
- Consistency (линеаризуемость): любое чтение видит последнюю запись.
- Availability: каждый запрос к живому узлу получает ответ (не ошибку).
Разделение сети в распределённой системе неизбежно, поэтому реальный выбор: при разделении жертвуем консистентностью (AP) или доступностью (CP). Без разделения можно иметь и то, и другое.
Примеры: системы с кворумом и лидером (etcd, ZooKeeper) ведут себя как CP: без большинства узлов запись отклоняется. Cassandra и DynamoDB по умолчанию ближе к AP и eventual consistency, хотя уровень настраивается.
PACELC
Уточняет CAP: если есть разделение (P), выбираем между A и C; иначе (Else) выбираем между задержкой (Latency) и консистентностью (Consistency). Синхронная репликация даёт консистентность ценой задержки.
Модели консистентности
- Строгая (линеаризуемость): как будто одна копия данных.
- Read-your-writes: пользователь видит свои записи.
- Монотонное чтение: не видим данные «откатывающимися назад».
- Причинная: если B следует из A, все видят A раньше B (ответ после вопроса).
- Eventual consistency: если записи прекратятся, реплики со временем сойдутся. Лайки, счётчики просмотров, ленты.
Кворумы
N реплик, запись подтверждается W узлами, чтение опрашивает R узлов. Если R + W > N, множества пересекаются и чтение увидит последнюю подтверждённую запись. Типично N=3, W=2, R=2. W=1, R=1 даёт скорость, но возможны устаревшие чтения.
Распределённые транзакции
2PC (двухфазный коммит): координатор спрашивает всех «готовы?», затем говорит «коммит». Даёт атомарность, но блокирует ресурсы, и при падении координатора участники зависают. В микросервисах применяют редко.
Сага: последовательность локальных транзакций, у каждой есть компенсирующее действие. Например: создать заказ → списать деньги → зарезервировать товар. Если резерв не удался, выполняются компенсации: вернуть деньги, отменить заказ. Два стиля:
- хореография: сервисы реагируют на события друг друга;
- оркестрация: центральный оркестратор говорит, кому что делать (проще отлаживать).
Сага не даёт изоляции: промежуточные состояния видны, поэтому вводят статусы вроде «ожидает оплаты».
Идемпотентность и ключи идемпотентности
Клиент отправляет Idempotency-Key с запросом платежа. Сервер запоминает результат по ключу, и повтор после таймаута не спишет деньги дважды. Это стандартная практика платёжных API.
Конфликты при записи в несколько мест
- Last write wins по метке времени: просто, но теряет данные и зависит от часов.
- Векторные часы: обнаруживают конкурирующие версии.
- CRDT: структуры, которые сливаются без конфликтов (счётчики, множества), используются в совместном редактировании.
Частые ошибки
- Говорить «выбираем CA» для распределённой системы.
- Путать C из CAP с C из ACID: в ACID это соблюдение ограничений целостности, а не свежесть реплик.
- Использовать 2PC между десятком микросервисов.
- Забыть про компенсации в саге.
Что спросят на собесе
- Объясните CAP на примере.
- Что такое eventual consistency и где она допустима.
- Как сделать перевод денег между сервисами без 2PC.
- Как работают кворумы и что значит R + W > N.
- Как защититься от двойного списания.