главная/Кворум и согласованные данные
Кворум чтение

Кворум и согласованные данные

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

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

Представим, что у нас есть 3 сервера базы:

  • Node A
  • Node B
  • Node C

Пользователь А обновляет баланс счёта на Node A.

Через 1 мс пользователь B делает запрос, но попадает на Node C, где данные ещё не успели обновиться.

В итоге:

  • A видит баланс 50 ₽
  • B видит старое значение 100 ₽

Система не линеаризуема, потому что пользователи видят разные версии данных.

Как кворум решает эту проблему

Пример:

У нас 3 реплики.

  • Для записи мы требуем W = 2 подтверждения.
  • Для чтенияR = 2.

Общее правило:

Если W + R > N (где N — число реплик), система гарантирует линеаризуемость.

Пример шаг за шагом

Пользователь А делает запись:

UPDATE balance = 50

Запись отправляется на все 3 реплики, но считается успешной, когда 2 из 3 подтвердят (например, A и B). Node C может обновиться чуть позже.

Пользователь B делает чтение:

SELECT balance

Клиент читает данные с 2 из 3 узлов (например, B и C).
Даже если C ещё не обновился, B вернёт новое значение — и система выберет самую свежую версию.

Результат:

Оба пользователя видят баланс 50 ₽.

Когда W + R > N, хотя бы одна реплика будет общей между чтением и записью.
Это значит, что чтение обязательно увидит последнюю записанную версию — и данные останутся согласованными.

Кворум делает так, что чтение и запись всегда пересекаются хотя бы на одной “честной” реплике.
Это гарантирует, что даже при задержках синхронизации, пользователи не увидят старые данные.

Нарушение линеаризуемости

Пример:

У нас есть три реплики (три узла базы данных):

  • Реплика 1
  • Реплика 2
  • Реплика 3

Начальное значение переменной x = 0.

Клиент отправляет запрос set x = 1 на все 3 узла.

  • Реплика 1 обновилась быстро
  • Реплика 2 и 3 — пока ещё нет (задержка в сети)

Запись считается успешной, когда 2 из 3 реплик (то есть кворум W = 2) подтвердили.
Но на самом деле обновление ещё не дошло до всех.

Клиент A запрашивает значение x.

Он читает данные из двух узлов (R = 2), например:

  • Реплика 1 (уже обновилась) → x = 1
  • Реплика 2 (ещё нет) → x = 0

Чтобы получить итог, клиент выбирает самую свежую версию — видит x = 1.

Теперь клиент B отправляет свой запрос чуть позже во времени, после клиента A.
Он тоже читает из двух узлов (R = 2), но попадает, например, на:

  • Реплика 2 → x = 0
  • Реплика 3 → x = 0

И получает x = 0.

Что пошло не так

Хотя клиент B обращается позже, он видит старые данные,
а клиент A, который читал раньше, — уже новые.

Это нарушает линеаризуемость,
потому что операции происходят не в логическом порядке времени.

Проблема не в том, что кворум “неправильный”.
Проблема в сетевых задержках и разных путях доставки данных.

Даже если формула кворума W + R > N выполняется,
реальные запросы могут попасть на разные комбинации узлов,
и часть из них ещё не получит свежие данные.

Представь трёх продавцов, которые делят склад:

  1. Ты обновляешь цену товара до 100₽ и говоришь двум продавцам из трёх.
  2. Первый продавец уже обновил, второй ещё нет, третий не в курсе.
  3. Покупатель А спрашивает цену у продавца 1 и 2 → видит 100₽.
  4. Покупатель Б чуть позже спрашивает у продавца 2 и 3 → видит 80₽.

Хотя Б пришёл позже, он видит старую цену — потому что спросил не у тех.
Вот это и есть нелинеаризуемость при строгом кворуме.

Как это исправить

Сделать чтение синхронным и объединяющим
— Клиент должен не просто брать данные с двух узлов,
а объединять версии и разрешать конфликты чтения (read repair).

Добавить “чтение через лидера” (read-from-leader)
— Все записи и чтения идут через один главный узел,
который гарантирует порядок операций.

Использовать консенсус (Raft / Paxos)
— Тогда все узлы применяют операции строго в одном и том же порядке.
Это дороже по времени, но зато гарантирует линеаризуемость.

Даже при правильном кворуме данные могут выглядеть “перепутанными во времени”,
потому что сеть не синхронна.
Чтобы это исправить - нужно либо читать только с лидера, либо синхронизировать версии при чтении.