Учебник веб-разработки
Разделы учебника
На этой странице

VIII. Системы хранения

Распределённый SQL: реплики, шарды и гарантии

Оглавление · SQL/NoSQL/NewSQL

Репликация создаёт копии данных, шардирование делит набор по узлам. Это разные операции: у каждого шарда могут быть свои реплики. Распределённый SQL добавляет SQL и транзакционный протокол поверх этой структуры; слово NewSQL само по себе не задаёт ни уровень изоляции, ни поведение при partition.

Цена распределения

Один SELECT по ключу может обратиться к одному range; JOIN или транзакция по разным ranges потребует сети и координации. Placement данных и replica locations влияют на latency. Нельзя одновременно обещать любой межрегиональный порядок записи, отсутствие сетевого ожидания и доступность при любом разделении. CAP относится к выбору между доступностью и согласованностью при partition, а не к универсальному меню «выбрать любые две буквы».

Исходник схемы
flowchart TD
  SQL[SQL gateway] --> R1[Range A]
  SQL --> R2[Range B]
  R1 --> A1[Реплика A1]
  R1 --> A2[Реплика A2]
  R1 --> A3[Реплика A3]
  R2 --> B1[Реплики другого range]

В CockroachDB ranges реплицируются с Raft. Большинство voting replicas нужно для согласования прогресса: три голоса дают quorum два. Потеря одного узла может быть переносима при правильном placement, но потеря большинства range останавливает его записи. Репликация одного range ещё не описывает атомарность транзакции нескольких ranges; для неё есть отдельный transaction layer.

Изоляция и наблюдаемый порядок

Serializable означает эквивалентность успешно завершённых транзакций некоторому последовательному порядку. Linearizability/strict serializability дополнительно связывают порядок с реальным временем завершения операций. Snapshot и stale follower read имеют другой контракт свежести. Проверяйте конкретную операцию и настройки, а не приписывайте всем чтениям одного продукта одну гарантию.

Система может вернуть retryable transaction error. Повторять надо всю транзакцию со свежими чтениями и ограниченным бюджетом, без неподконтрольного внешнего эффекта внутри callback. Дедупликация по ключу из транзакционной лаборатории помогает на прикладной границе, но её SQL и стратегия блокировок PostgreSQL не переносятся автоматически на другую СУБД.

Локальная модель quorum

Сохраните блок и выполните Node. Это проверка множества голосов, не реализация Raft: нет журналов, сроков, выборов лидера и сети.

import assert from "node:assert/strict";
const voters = new Set(["A", "B", "C"]);
const quorum = Math.floor(voters.size / 2) + 1;
function canCommit(available) {
  return [...new Set(available)].filter((node) => voters.has(node)).length >= quorum;
}
assert.equal(canCommit(["A", "B"]), true);
assert.equal(canCommit(["A"]), false);
assert.equal(canCommit(["A", "A"]), false);
assert.equal(canCommit(["A", "B", "observer"]), true);
console.log("Three voters: quorum two; duplicate votes do not count");

Упражнение на partition: группа AB и изолированный C не могут обе независимо подтверждать новые записи одного Raft group. Если приложение принимает локальный «успех» C без нужного quorum, оно изменило гарантию. Наличие старой копии данных не даёт права объявить новую запись committed.

Проектирование опыта на изолированном кластере

Это задание на проектирование опыта: готовый кластер репозиторий не поставляет. До начала нужны права на свои узлы, учебная схема, сохранённый набор данных, два клиентских подключения и безопасный способ имитации отказа. Среда готова, когда все узлы доступны, обычная запись прочитана обоими клиентами и безопасный restart одного узла проверен.

Когда доступен отдельный учебный CockroachDB-кластер, зафиксируйте версию, число voters и placement для range. Используйте только вымышленные записи. Развёртывание дополнительного кластера не является изменением Atmanki.

  1. Создайте таблицу и один объект, проверьте обычный read/write и фактическую изоляцию session. Сохраните время начала/окончания и transaction IDs.
  2. Два клиента читают одну version и условно меняют её. Одна операция должна выиграть; отдельно фиксируйте retry и бизнес-конфликт версии.
  3. Остановите один свой узел. Проверьте quorum конкретного range, новое соединение, подтверждённую запись и повтор после неизвестного результата.
  4. В изолированной копии уберите доступность большинства range. Зафиксируйте отказ/timeout записи; не называйте отказом узла любую потерю quorum и наоборот.
  5. Сравните strong read и явно выбранный historical/follower read. Старое значение во втором случае оценивайте по заявленному freshness contract.
  6. Восстановите узлы и проверьте согласованные данные. Replication не заменяет backup: ошибочное удаление также может разойтись по репликам.

Приёмка: последовательность операций и наблюдений, настройки, retry budget, placement и фактический quorum. Это план выполняемого опыта с критериями, не отчёт об уже испытанном кластере. Однопроцессная модель подтверждает лишь арифметику quorum. Для Atmanki один PostgreSQL на VPS проще в эксплуатации; распределение вводят под измеренные требования объёма, отказов или регионов.

Источники: репликация CockroachDB, транзакции, Raft.