Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Quorum NWR: чтение и запись в распределенных хранилищах

Quorum NWR: как число реплик управляет согласованностью

Модель NWR: реплики N, кворум записи W и чтения R — формула строгости, примеры настроек Cassandra и DynamoDB, разбор конфликтов и read repair.

Прямой ответ

NWR — способ настроить строгость репликации тремя числами: N — сколько реплик хранит данные, W — сколько реплик должны подтвердить запись, R — сколько реплик опрашивается при чтении. Главное правило: если W + R > N, множества записи и чтения пересекаются, и чтение всегда увидит последнюю подтверждённую запись. Это и есть строгая (линеаризуемая по одному ключу) согласованность в рамках ключа.

Типовые конфигурации при N = 3:

N W R Поведение
3 3 1 Быстрое чтение, медленная запись, отказ одной реплики блокирует запись
3 2 2 Пересечение гарантировано, сбалансированный вариант (кворум)
3 1 1 Максимальная скорость, чтения могут вернуть устаревшие данные
3 3 3 Максимальная строгость, минимальная доступность

Важно понимать ограничения:

  • Формула не равна ACID. W + R > N даёт «последнюю записанную версию» по ключу, но не транзакции между ключами и не разрешение причинных связей — для этого нужны векторные часы и, при необходимости, LWW- или CRDT-стратегии.
  • «Слоупай»-эффект. Кворум не спасает от чтения устаревшей версии, если часть реплик отвечает медленно: клиент получает R ответов, а самая свежая реплика ещё не ответила. Лечится read_repair и speculative retry.
  • Расположение реплик. Реплики размещаются по кольцу с помощью консистентного хеширования, поэтому настройка N влияет и на распределение нагрузки.

Практика: настройки в Cassandra

Уровни согласованности. В Cassandra кворум задаётся уровнем: ONE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL. QUORUM вычисляется как (RF / 2) + 1, где RF — replication factor (то есть N).

-- таблица с RF=3 в датацентре dc1
CREATE KEYSPACE shop
  WITH replication = {'class': 'NetworkTopologyStrategy', 'dc1': 3};

-- строгая запись и чтение в пределах датацентра
INSERT INTO shop.orders (id, total) VALUES (1001, 250)
  USING CONSISTENCY LOCAL_QUORUM;
SELECT total FROM shop.orders WHERE id = 1001
  USING CONSISTENCY LOCAL_QUORUM;

Практические рекомендации:

  1. LOCAL_QUORUM вместо QUORUM для многодатацентровых кластеров: запись остаётся в своём DC, задержки предсказуемы, а кросс-DC трафик не влияет на p99.
  2. W + R > N проверяйте по факту. LOCAL_QUORUM на запись + LOCAL_QUORUM на чтение при RF=3 даёт 2 + 2 > 3 — пересечение есть. А LOCAL_ONE + LOCAL_ONE = 1 + 1 = 2, что не больше 3 — строгости нет.
  3. Read repair и anti-entropy. Расхождения между репликами устраняются фоновым repair; без него устаревшие версии могут жить долго, особенно при редких записях в ключ.
  4. Мониторинг. Следите за pending-задачами repair, числом таймаутов и долей операций с уровнем ниже кворума — это источник «фантомных» несоответствий, которые потом разбирают как инциденты.

Как выбирать уровень под сценарий. Практический подход — разделить операции на три класса. Первый: деньги, статусы заказов, инвентарь — здесь кворум на запись и кворум на чтение обязательны, а экономия на задержке недопустима. Второй: персональные настройки, содержимое профиля — допустима запись кворумом и чтение LOCAL_ONE, потому что расхождение в сотни миллисекунд не влияет на бизнес-логику. Третий: счётчики показов, телеметрия, логи — достаточно LOCAL_ONE на запись, а потеря отдельных значений приемлема; такой поток дешевле и не создаёт нагрузки на кворум.

Проверка конфигурации тестом. Полезно держать автоматическую проверку, что выбранная пара уровней действительно даёт строгость: записываем уникальное значение кворумом, затем читаем выбранным уровнем в цикле и фиксируем долю устаревших ответов. Если доля ненулевая при ожидаемой строгости — конфигурация не соответствует требованиям, и это видно до инцидента, а не во время него.

Практический сценарий: чтение вернуло старое значение при кворуме

Контекст: Cassandra, RF=3, запись LOCAL_QUORUM, чтение LOCAL_ONE. Пользователь обновил адрес, но в интерфейсе остался старый.

  1. Диагностика. Проверяем уровень согласованности в коде: чтение оказалось LOCAL_ONE, то есть W + R = 2 + 1 = 3, что не больше N=3 — формальной гарантии нет.
  2. Проверка реплик. Запрос SELECT ... USING CONSISTENCY ALL (или nodetool getendpoints + точечные запросы) показывает: одна реплика содержит новую версию, две — старую, запись на третью не успела из-за таймаута.
  3. Исправление. Переводим чтение на LOCAL_QUORUM; включаем read_repair для горячих таблиц; добавляем speculative retry для снижения хвостовых задержек.
  4. Регресс. Тест на несогласованность: пишем значение, читаем кворумом, сверяем; повторяем под нагрузкой. Такую проверку удобно держать в интеграционных тестах на Testcontainers.
  5. Компромисс. Для экранов, где свежесть не критична (лента, аналитика), осознанно оставляем слабые чтения и фиксируем решение в ADR.

Результат: строгие сценарии переведены на кворум, слабые чтения остались, но задокументированы. Вывод: NWR — это не «включить по умолчанию», а явный выбор по каждому сценарию; см. также модели согласованности.

FAQ

Что значит «кворум» на практике? Это минимальное число реплик, которые должны ответить, чтобы операция считалась выполненной. Для N=3 кворум равен 2, для N=5 — 3. Кворум гарантирует пересечение с любым другим кворумом, что и обеспечивает видимость последней записи.

Можно ли сделать W=1, R=1 и всё равно получить согласованность? Нет. 1 + 1 = 2, что не больше N=3, поэтому чтение может попасть на реплику, которая не участвовала в записи. Такая конфигурация приемлема для счётчиков показов или логов, но не для балансов и статусов заказов.

Как NWR соотносится с теоремой CAP? NWR — это «ручка» внутри AP-архитектуры: поднимая W и R до кворума, вы жертвуете доступностью при отказах ради согласованности. Полностью CP-системы (например, на Raft) не дают такой настройки — они всегда требуют большинства. Обзор компромисса — в статье про теорему CAP.

Что делать с конфликтами одновременных записей? Кворум не разрешает конфликты, он лишь гарантирует видимость. Стратегии разрешения — last-write-wins по таймстампу, приложение-уровневое слияние или CRDT для структур, которые должны сходиться автоматически.

Источники: Cassandra docs (consistency), Wikipedia (quorum), Amazon Dynamo paper (SOSP 2007) — ссылки в шапке.