Про 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 — когда настраиваемая согласованность и масштаб записи оправданы, а когда нужна классическая база.