Реализация строгой консистентности: практические паттерны

Как нам заставить кучу серверов вести себя как один? Есть три основных подхода.

Паттерн 1: Single-Leader (Синхронная репликация)

Самый простой в лоб:

  1. Запись идет только в Мастер.
  2. Мастер синхронно ждет ответа от всех Слейвов.
  3. Только потом отвечает клиенту.

Минус: Если один слейв в Австралии «моргнул» и отвалился от сети, зависнет вся система (потеря доступности).

Паттерн 2: Quorum (Кворум)

Используется в Dynamo-подобных базах (Cassandra, Riak, DynamoDB). В подходе выше мы сильно зависим от одного узла (мастера). Кворумный подход решает эту проблему: чаще всего в таком кластере все узлы равны (Leaderless архитектура), и мы отправляем запросы на чтение и запись сразу в несколько узлов.

Про leaderless архитектуру можешь прочесть в этом посте

Чтобы достичь строгой консистентности, нам не обязательно ждать все узлы. Достаточно математического большинства.

Формула строгой консистентности: W + R > N

  • N — общее количество реплик, хранящих данные.
  • W (Write Quorum) — количество узлов, которые должны подтвердить успешное сохранение, чтобы запись считалась успешной.
  • R (Read Quorum) — количество узлов, которые нужно опросить при чтении.

Почему это работает?

Представь, что у нас кластер из 5 узлов (N=5). Если мы подтверждаем запись на 3 узлах (W=3) и читаем с 3 узлов (R=3), то по базовым законам математики эти два множества обязательно пересекутся минимум на одном узле.

Как система понимает, где новые данные?
При чтении координатор опрашивает R узлов и получает от них данные вместе с их версиями (таймстемпами или векторными часами, о которых мы говорили в прошлом видео).

Тот самый узел на пересечении гарантированно отдаст самую свежую версию данных. Система сравнивает таймстемпы, берет победившую версию (допустим, через Last Write Wins) и отдает клиенту. Остальные узлы со старыми данными заодно обновляются в фоне (это называется Read Repair).

Про то, как системы могут чинить себя сами (read repair) можешь прочесть в этом посте.

Конфигурации кворума под разные задачи:

  • Сбалансированная (N=5, W=3, R=3): Классика. Система переживет падение любых двух узлов, сохранив строгую консистентность.
  • Быстрая запись, тяжелое чтение (N=5, W=1, R=5): Запись моментальная (хватит одного узла), но чтение дорогое и медленное, так как нужно опросить весь кластер. И если хотя бы один узел упадет — чтение сломается.
  • Быстрое чтение, тяжелая запись (N=5, W=5, R=1): Идеально для данных, которые редко меняются, но постоянно читаются (например, справочники).

Плюсы: Нет единой точки отказа (как в Master-Slave). Система отлично переживает падение части узлов (High Availability).

Минусы: Время ответа (latency) при записи или чтении всегда равно времени ответа самого медленного узла в кворуме. Если один сервер под нагрузкой тупит, будет тупить весь запрос клиента.

Паттерн 3: Алгоритмы Консенсуса (Raft / Paxos)

Данный подход часто используется в индустрии для критически важных метаданных (используется в etcd, Consul, ZooKeeper). Если первый паттерн (Single-Leader) был «глупым» и система ложилась при падении мастера, то алгоритмы консенсуса — это «умный» Single-Leader без единой точки отказа.

Как это работает

  1. Выбор лидера: узлы общаются между собой и выбирают лидера. Остальные становятся последователями. Если Лидер умирает, кластер это замечает по отсутствию пингов (heartbeats) и за миллисекунды проводит новые выборы.
  2. Журнал изменений: все изменения выстраиваются в строгую очередь в логе. Лидер — единственный, кто имеет право писать в этот журнал. Это дает нам ту самую гарантию порядка операций во времени (Linearizability), исключая любые конфликты.
  3. Кворумный коммит: когда приходит запись, лидер предлагает ее кластеру. Как только большинство узлов (кворум) ответило «я успешно записал это в свой лог», Лидер считает операцию закоммиченной и отвечает клиенту успехом.

То есть алгоритмы консенсуса объединяют плюсы первых двух подходов: строгий порядок от мастера (нет конфликтов при записи) и надежность кворума (можно пережить падение части машин).

Плюсы: консистентность, полная защита от split-brain (в кластере математически не может быть двух лидеров одновременно), автоматическое восстановление при сбоях.

Минусы: дорого по производительности из-за постоянных сетевых переписок для поддержания консенсуса.