← назад к разделу

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

Replication factor: сколько копий

Replication factor (RF) — число копий каждой партиции в кластере. RF = 3 означает, что данные лежат на трёх разных узлах. Это фундамент: чем больше копий, тем выше надёжность и доступность, но тем дороже запись (её надо разнести по большему числу узлов).

RF задаётся на уровне пространства ключей (keyspace) и стратегии репликации. Для боевых систем берут NetworkTopologyStrategy, которая раскладывает копии с учётом дата-центров — например, по три копии в каждом из двух ЦОД.

Уровень согласованности: сколько реплик ответят

RF — это сколько копий существует. Уровень согласованности (consistency level, CL) — сколько из них должны ответить, чтобы операция считалась успешной. И это задаётся на каждый запрос, отдельно для чтения и для записи. Основные уровни:

УровеньЧто требуетСмысл
ONEответ одной репликибыстро, но чтение может отставать
QUORUMответ большинства реплик (RF/2 + 1)баланс скорости и свежести
LOCAL_QUORUMбольшинство реплик в локальном ЦОДкворум без похода в другой ЦОД
ALLответ всех репликмаксимум согласованности, минимум доступности

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

Главное правило: R + W > RF

Вот ключ ко всему. Строгую (немедленную) согласованность в Cassandra дают не сами по себе уровни, а их сочетание при чтении и записи. Правило простое:

Если число реплик, подтверждающих запись (W), плюс число реплик, отвечающих на чтение (R), больше replication factor — чтение гарантированно увидит последнюю запись.

Почему: если R + W > RF, множества «кто подтвердил запись» и «кого спросили при чтении» обязательно пересекутся хотя бы на одной реплике — а значит, чтение зацепит свежую копию. Классический рецепт строгого чтения при RF = 3 — писать и читать с QUORUM (2 + 2 = 4 > 3). Если же и запись, и чтение идут с ONE (1 + 1 = 2 < 3), пересечение не гарантировано — получаем итоговую согласованность: данные сойдутся, но не мгновенно.

Главная ловушка новичка: считать Cassandra «всегда несогласованной». Это не так — она даёт строгую согласованность при QUORUM/QUORUM, просто заставляет вас осознанно выбрать баланс скорости и свежести под каждый запрос. Для баланса счёта — кворум; для ленты активности хватит ONE.

// Запись с кворумом
session.execute(
    insertStatement.setConsistencyLevel(ConsistencyLevel.QUORUM));

// Чтение с кворумом → вместе с записью QUORUM даёт строгое чтение при RF=3
session.execute(
    selectStatement.setConsistencyLevel(ConsistencyLevel.QUORUM));

Как реплики догоняют друг друга

Раз при ONE реплики временно расходятся, Cassandra их постоянно синхронизирует тремя механизмами:

  • hinted handoff — если реплика в момент записи недоступна, координатор сохраняет «подсказку» (hint) и, когда реплика вернётся, доигрывает пропущенные записи. Так короткие сбои узла не приводят к потере данных;
  • read repair — при чтении с нескольких реплик координатор замечает расхождения и на лету обновляет отставшие копии самой свежей версией;
  • anti-entropy repair — плановая фоновая сверка реплik (запускается администратором), которая находит и чинит расхождения, накопившиеся из-за долгих сбоев. Регулярный repair — обязательная часть эксплуатации Cassandra.

Вместе они реализуют «итоговую согласованность»: даже если данные разошлись, эти механизмы гарантированно сводят их обратно.

Облегчённые транзакции и их цена

А как же операции «сделать, только если ещё не сделано» — например, зарегистрировать пользователя с уникальным логином? Для таких случаев есть облегчённые транзакции (LWT, lightweight transactions) — условные операции вида INSERT ... IF NOT EXISTS или UPDATE ... IF column = ?. Под капотом они используют протокол консенсуса Paxos, чтобы обеспечить линеаризуемость в пределах одной партиции.

Но за это платят: LWT требует нескольких раундов между репликами и потому в разы медленнее обычной записи. Правило простое: LWT — для редких операций, где без строгого «сравни-и-запиши» никак (уникальность, защита от гонки), а не для массового потока. Если LWT нужны на каждом шагу — скорее всего, Cassandra выбрана не под ту задачу.

Чего Cassandra не гарантирует

Чтобы не ждать от базы того, чего в ней нет:

  • нет транзакций между партициями. Атомарно изменить данные в разных партициях (тем более разных таблицах) нельзя. Пакет (BATCH) даёт атомарность только в пределах одной партиции; между партициями он не транзакция, а лишь группировка.
  • нет отката и изоляции, как в SQL. Модель рассчитана на независимые записи по ключу, а не на сложные транзакционные сценарии.
  • удаление — это запись (tombstone), поэтому «удалять и вставлять» массово — антипаттерн.

Всё это — не недостатки, а прямая плата за масштаб записи и постоянную доступность. Если эта плата неприемлема — значит, задаче нужна PostgreSQL или MongoDB, а не Cassandra.

Коротко

  • Replication factor (RF) — сколько копий партиции существует; уровень согласованности (CL) — сколько реплик должны ответить, задаётся на каждый запрос отдельно для чтения и записи.
  • Строгое чтение даёт правило R + W > RF (классика — QUORUM на запись и чтение при RF = 3); ONE/ONE даёт итоговую согласованность. Cassandra не «несогласованная», а настраиваемая.
  • Реплики сводятся через hinted handoff, read repair и плановый anti-entropy repair (последний — обязателен в эксплуатации).
  • LWT (Paxos) дают строгое «сравни-и-запиши» в пределах партиции, но дороги — только для редких операций (уникальность).
  • Нет транзакций между партициями, нет отката/изоляции как в SQL, удаление — это tombstone. Это плата за масштаб.

Что почитать дальше

  • Архитектура Cassandra — как устроены кольцо, репликация и путь записи, на которых стоят эти гарантии.
  • Модель данных Cassandra — как проектировать таблицы под доступ по ключу раздела.
  • Cassandra, PostgreSQL или MongoDB — когда настраиваемая согласованность и масштаб записи оправданы, а когда нужна классическая база.