Как гарантировать консистентность в распределенной базе данных?

Гарантирование консистентности (согласованности) в распределённой базе данных — одна из наиболее сложных задач, связанная с тем, что данные реплицируются между узлами, работают в условиях возможных сетевых сбоев, задержек и конфликтов. Консистентность означает, что все узлы системы отражают одно и то же состояние данных в определённый момент времени (или через согласованный протокол). Чтобы обеспечить консистентность, используются различные подходы и техники в зависимости от архитектуры, требований к доступности и задержкам.

📐 Модели консистентности

Перед тем как говорить о техниках, нужно понимать уровни согласованности:

1. Строгая консистентность (Strong consistency)

Каждое чтение возвращает последнее успешное обновление. Это требует синхронной репликации — все узлы должны согласовать данные до того, как операция считается завершённой.

2. Согласованность по записи (Linearizability)

Каждая операция читается/записывается как будто в единой глобальной последовательности. Реализуется, например, через алгоритмы Raft или Paxos.

3. Последовательная консистентность (Sequential consistency)

Операции всех клиентов происходят в одинаковом порядке, но этот порядок может не совпадать с реальным временем.

4. Согласованность с запаздыванием (Eventual consistency)

Гарантируется, что все узлы в конечном итоге придут к одному состоянию, но в момент времени значения могут отличаться. Часто применяется в системах, где важна высокая доступность (например, Cassandra, DynamoDB).

⚙️ Техники обеспечения консистентности

1. Алгоритмы распределённого консенсуса

Используются для обеспечения единого порядка операций и выборов лидера.

🔸 Paxos

  • Обеспечивает согласованность, даже если некоторые узлы сбойны.

  • Сложный, но доказательно корректный.

🔸 Raft

  • Более понятный и применимый в практике аналог Paxos.

  • Используется в системах вроде Etcd, Consul, CockroachDB.

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

2. Синхронная репликация

  • При записи данные передаются на все реплики.

  • Подтверждение операции только после записи на все (или достаточное количество) узлов.

  • Пример: MongoDB с WriteConcern=majority, PostgreSQL с synchronous_standby_names.

Недостатки:

  • Высокая задержка

  • Возможность недоступности при потере части узлов

3. Двухфазный коммит (2PC)

Протокол используется для распределённых транзакций:

  • Фаза 1 (prepare): координатор отправляет запрос на подготовку к коммиту.

  • Фаза 2 (commit/abort): если все участники готовы — происходит коммит, иначе — откат.

Недостатки:

  • Уязвимость к сбоям координатора

  • Потенциальные блокировки ресурсов

4. Трёхфазный коммит (3PC)

Улучшение над 2PC с дополнительной фазой, чтобы избежать блокировок при сбоях. Однако редко применяется из-за сложности и меньшей эффективности.

5. CRDT и Operational Transformation

В eventual consistent системах для разрешения конфликтов используют:

#### **CRDT (Conflict-free Replicated Data Types)**
  • Специальные структуры данных, которые могут автоматически объединяться без конфликтов.

Пример: G-Counter, G-Set, LWW-Element-Set.

OT (Operational Transformation)

  • Применяется, например, в Google Docs: изменения упорядочиваются и трансформируются так, чтобы результат был одинаковым у всех пользователей.

6. Quorum-согласование

Подход из систем типа Dynamo (и Cassandra):

  • Операция считается успешной, если подтверждение получено от кворума узлов.

Пусть N — общее число реплик, W — число реплик, куда пишем, R — число реплик, откуда читаем. Чтобы обеспечить консистентность:

R + W > N

Это гарантирует пересечение между читаемыми и пишемыми узлами.

7. Версионность данных и разрешение конфликтов

В eventual consistent системах (как Dynamo) часто используются векторные часы (vector clocks) для отслеживания причинно-следственных связей. Конфликты могут разрешаться:

  • Автоматически (по времени, последнему значению)

  • На стороне приложения/пользователя

8. Изоляция транзакций (ACID)

В распределённых БД (например, Google Spanner) реализуются уровни изоляции:

  • Serializable — самая строгая (но дорогая)

  • **Snapshot Isolation
    **

  • Read Committed, **Read Uncommitted
    **

Spanner использует механизм TrueTime, чтобы обеспечить глобальный порядок транзакций (основан на GPS/Atomic clocks).

9. Техника "write-ahead log" (WAL)

  • Все изменения сначала пишутся в журнал (лог), а затем применяются к данным.

  • Позволяет восстановить консистентное состояние при сбоях.

Используется в PostgreSQL, HBase, Kafka и других системах.

🔄 Консистентность при репликации и шардировании

Репликация

  • Primary-Replica (Master-Slave) — записи идут в один узел, остальные читают.

  • Multi-Master — записи возможны в несколько узлов, требует сильных стратегий консистентности.

Шардирование

  • Каждая нода хранит часть данных.

  • Обеспечение консистентности требует координации между шардами (например, через 2PC или Raft).

📊 Выбор подхода зависит от:

  • требований к доступности (A в CAP-теореме),

  • допустимых задержек,

  • характера системы (OLTP vs OLAP),

  • объема и частоты операций.

В распределённых системах почти всегда приходится жертвовать чем-то: строгой консистентностью ради доступности, или наоборот — снижать доступность ради ACID-гарантий.