«Возьмём Cassandra, она же масштабируется» — фраза, за которой часто нет понимания, чем Cassandra отличается от привычной базы и какой ценой даётся её масштаб. Cassandra решает вполне конкретный класс задач и в других делает только хуже. Разберём, что она такое, и сравним с двумя другими популярными вариантами — реляционной PostgreSQL и документной MongoDB (их сравнение между собой — в отдельной статье). Это статья про выбор; подробный разбор устройства Cassandra — в отдельном разделе.
Вопрос один, а пути разные: PostgreSQL находит клиента в индексе и добирает строки, MongoDB отдаёт документ целиком вместе с позициями, Cassandra по ключу раздела попадает сразу на нужный узел и читает готовый порядок. Цена видна на втором вопросе: реляционной и документной базе хватает ещё одного индекса на тех же данных, а Cassandra под каждый новый вопрос нужна своя таблица с копией — поэтому сначала выписывают запросы и только потом рисуют таблицы.
Что такое Cassandra
Лента событий растёт на миллионы записей в сутки, и один PostgreSQL перестаёт успевать писать. Apache Cassandra сделана ровно под это: распределённая NoSQL-база с моделью «широких строк» (wide-column), где запись размазана по десяткам узлов без главного. Название путает: с колоночными базами вроде ClickHouse, где значения одного столбца лежат на диске подряд, у неё общего мало. Здесь «широкая строка» означает другое — у каждой строки может быть свой набор колонок, а физически рядом хранятся строки одной партиции. Она выросла из двух идей: модель данных взята у Google Bigtable, а способ распределения по узлам — у Amazon Dynamo. Отсюда две её определяющие черты:
- Модель данных «широкая строка». Данные лежат в таблицах с языком запросов CQL, но устроены иначе, чем в реляционной базе: по ключу раздела (partition key) хранится группа строк, сгруппированных и упорядоченных колонками кластеризации. Одна партиция может содержать от одной строки до многих тысяч упорядоченных строк — это и называют wide-column. Проектируют такие таблицы не «от сущностей», а от запросов.
- Отсутствие главного узла (masterless). В кластере Cassandra все узлы равноправны — нет ведущего, к которому идут все записи. Данные распределены по кольцу по хешу ключа раздела, каждый узел принимает и чтение, и запись. Это и даёт её главное свойство.
Главное свойство — линейная горизонтальная масштабируемость записи. Добавили узлы — пропорционально выросла пропускная способность, без единой точки, в которую упирается нагрузка. Плюс встроенная репликация по нескольким дата-центрам в режиме «активный-активный»: кластер продолжает принимать запись, даже если целый дата-центр отвалился.
Cassandra против PostgreSQL
PostgreSQL — реляционная база с одним первичным узлом на запись. Её сила — строгая схема, JOIN между таблицами, произвольные запросы и ACID-транзакции: перевод денег между счетами либо проходит целиком, либо не проходит вовсе.
Cassandra устроена ровно наоборот там, где это нужно для масштаба:
- Нет
JOIN, и с произвольными запросами туго. Запрос должен бить по ключу раздела; «а покажи теперь под другим углом» без заранее спроектированной под этот угол таблицы работает плохо. С версии 5.0 к этому добавились вторичные индексы нового образца — SAI, storage-attached indexes: они позволяют спросить по обычной колонке, не заводя вторую таблицу. Но запрос без ключа раздела всё равно опрашивает узлы кластера и собирает ответ по кусочкам, так что для частого чтения по-прежнему делают отдельную таблицу, а индекс оставляют для редких обращений. - Нет полноценных транзакций между строками. Есть облегчённые условные операции, но переводить деньги между счетами, как в PostgreSQL, сегодня здесь нельзя. Слово «сегодня» тут не для красоты: общие транзакции для Cassandra разрабатывают (замысел называется Accord), и через пару релизов формулировку придётся переписывать. Пока же планировать на это нельзя.
- Согласованность настраивается на каждый запрос. Cassandra не «всегда несогласованная»: уровень согласованности задают отдельно для чтения и записи. Читаете и пишете с одной репликой — быстро, но чтение может ненадолго отставать (итоговая согласованность). Читаете и пишете с кворумом — получаете строгое чтение (правило
R + W > RF), но с большей задержкой. Это осознанный выбор баланса скорости и свежести, а не жёсткая жертва согласованностью.
Вывод: PostgreSQL берут, когда важны транзакции, связи и гибкие запросы на данных, помещающихся на один мощный сервер (плюс реплики для чтения). Cassandra — когда объём записи и требование «всегда доступна» перерастают то, что тянет один первичный узел.
Cassandra против MongoDB
MongoDB — документная база: данные хранятся как JSON-подобные документы с гибкой схемой. Обе — NoSQL и обе умеют масштабироваться шардированием, но природа разная:
- Топология. У MongoDB есть первичный узел на каждый шард (replica set с автоматическим переключением при сбое). У Cassandra первичного узла нет вообще — запись принимает любой узел. Для сценария «активная запись в нескольких регионах одновременно» Cassandra устроена удобнее.
- Запросы и индексы. MongoDB богаче: вторичные индексы, гибкие запросы по полям, агрегации, многодокументные транзакции. Cassandra в запросах жёстче — всё вокруг ключа раздела.
- Ниша. MongoDB — универсальная база под изменчивую предметную область и разнообразные запросы. Cassandra — узкий инструмент под огромный поток записи и постоянную доступность.
Если упрощать: MongoDB гибче в модели и запросах, Cassandra — сильнее в масштабе записи и мультирегиональной доступности.
Модель данных: сначала запросы, потом таблицы
Главная ошибка новичка в Cassandra — проектировать таблицы как в реляционной базе, «по сущностям», а потом удивляться, что нужный запрос невозможен. В Cassandra проектирование идёт от запросов: сначала выписывают, какие именно чтения будут в проде, и под каждое строят свою таблицу, сознательно дублируя данные (денормализация — норма, а не грех). Ключ раздела (partition key) определяет, на каком узле лежат данные и по какому ключу их можно достать; колонки кластеризации задают порядок внутри раздела.
Отсюда практическое правило: если заранее не известно, как будут спрашивать данные, или запросы разнообразны и меняются — Cassandra противопоказана. Она вознаграждает предсказуемые обращения по ключу и наказывает за произвольную аналитику.
Чем три базы различаются
| PostgreSQL | MongoDB | Cassandra | |
|---|---|---|---|
| Модель | реляционная (таблицы) | документная (JSON) | широкая строка (wide-column) |
| Схема | строгая | гибкая | полустрогая, под запросы |
| Топология записи | один первичный узел | первичный на шард | без главного узла |
| Транзакции | полный ACID | на документ и многодок. | ограниченные |
JOIN/произвольные запросы | да | ограниченно | нет |
| Согласованность | зависит от уровня изоляции, на репликах чтение отстаёт | настраиваемая | настраиваемая, по умолчанию итоговая |
| Сильна в | связи, транзакции, аналитика | гибкая модель, разные запросы | огромный поток записи, постоянная доступность |
Когда что брать
- PostgreSQL — выбор по умолчанию: связи, транзакции, гибкие запросы, данные на одном сервере. Большинству сервисов её хватает надолго.
- MongoDB — изменчивая предметная область, документная модель, разнообразные запросы; нужен горизонтальный рост, но не экстремальный поток записи.
- Cassandra — телеметрия и события интернета вещей, ленты активности, логи, счётчики, сообщения: огромный поток записи, предсказуемые обращения по ключу, требование «всегда доступна» и запись в нескольких регионах сразу.
Слева направо: сначала отсеивает поток записи, дальше вопрос о предсказуемости запросов, и только на последнем шаге решают транзакции; нижняя строка это путь по умолчанию.
Сколько стоит владение Cassandra
Половина решения — не возможности, а эксплуатация, и здесь Cassandra дороже обеих альтернатив.
Минимальный кластер — три узла, и это не рекомендация, а следствие модели: при трёх копиях и кворуме двух кластер переживает потерю одного узла. Один или два узла не дают ни отказоустойчивости, ни смысла всей архитектуры. Для мультирегиональной установки это уже три узла в каждом ЦОД.
Обязательное регулярное восстановление. Плановую сверку реплик (nodetool repair) надо запускать чаще, чем истекает срок хранения меток удаления, иначе удалённые данные воскресают. Это нагрузка на диск и сеть, расписание и присмотр — то, чего в PostgreSQL нет вовсе.
Уплотнение как отдельная забота. Выбор стратегии уплотнения, запас свободного места (размерная стратегия в худшем случае требует столько же места, сколько занимает таблица), контроль числа файлов на чтение. Ошибка здесь выглядит как «кластер постепенно замедляется», и лечится она не индексом, а перенастройкой хранения.
Настройка виртуальной машины Java. Cassandra написана на Java, и её задержки на хвосте распределения определяются сборкой мусора; размер кучи и выбор сборщика — часть обязательной настройки, а не тонкая оптимизация.
Две системы вместо одной. Аналитику по данным в Cassandra не сделать: произвольных запросов там нет. Поэтому рядом почти всегда стоит вторая система — ClickHouse, Spark или хранилище данных, — куда поток уезжает для отчётов. То есть «взяли Cassandra» практически никогда не означает «одна база»: это Cassandra плюс конвейер плюс аналитическое хранилище.
Отсюда честный расчёт: сравнивать надо не «PostgreSQL против Cassandra», а «один сервер PostgreSQL с репликой» против «шесть узлов Cassandra плюс аналитическое хранилище плюс человек, который умеет это обслуживать».
Пороги в цифрах
Разговоры про «огромный поток записи» становятся полезными, когда превращаются в числа. Ориентиры такие.
Один сервер PostgreSQL на обычном железе с быстрыми дисками принимает порядка нескольких тысяч транзакций записи в секунду при коротких транзакциях, и это можно поднять пакетами, COPY и отказом от лишних индексов — в разы. Десятки тысяч записей в секунду на одну машину — уже задача, которая требует настройки и обычно партиционирования; сотни тысяч — не про один сервер.
По объёму: таблицы до сотен гигабайт PostgreSQL обслуживает без разговоров, единицы терабайт требуют партиционирования и внимания к обслуживанию, десятки терабайт на одном сервере — это область, где эксплуатация становится основной работой.
Поэтому практический порог звучит так: пока поток записи измеряется тысячами в секунду, а объём — гигабайтами, вопрос о Cassandra не стоит. Он появляется, когда и запись не влезает в одну машину, и данные растут линейно и безгранично, и запросы известны заранее. Три условия одновременно, а не одно.
С кем на самом деле сравнивать
Если решение в пользу колоночного хранилища с ключом раздела принято, Cassandra — не единственный вариант, и остальные стоит знать.
ScyllaDB — совместимая по языку запросов и драйверам реализация на C++ без виртуальной машины Java: те же модель и API, заметно меньше узлов на тот же поток и нет настройки сборки мусора. Обычно её и рассматривают первой вместо Cassandra.
DynamoDB (в облаке Amazon) — та же идея «ключ раздела плюс ключ сортировки», но как управляемая услуга: эксплуатации нет вовсе, зато есть привязка к провайдеру и оплата по операциям, которая на больших потоках считается заранее.
YDB — распределённая база с похожей раскладкой по ключу, но с транзакциями и SQL-подобным языком; интересна, когда нужен масштаб записи, но отказываться от транзакций не хочется.
И рядом стоит помнить, что MongoDB тоже шардируется, и вопрос выбора ключа шарда там ровно тот же: он определяет, как данные лягут по узлам, меняется с трудом и неудачный выбор даёт перекос, когда один шард получает всю нагрузку. То есть «шардирование из коробки» не избавляет от главного проектного решения, а только прячет его до первого перекоса.
Типичные ошибки
- Берут Cassandra «ради масштаба» без потока записи. Для сервиса на один сервер это лишь потеря транзакций,
JOINи гибких запросов взамен ненужного масштаба. - Проектируют таблицы Cassandra как реляционные. Без проектирования от запросов нужные чтения оказываются невозможны или дороги.
- Ждут от Cassandra транзакций и аналитики. Перевод денег и произвольные отчёты — не её задача; для этого рядом держат PostgreSQL.
- Путают «NoSQL» с «современно». MongoDB и Cassandra — разные инструменты под разные задачи, а не моднее реляционной базы.
Глубже: во что обходится вторая базарасширенное
Все развилки этого раздела сравнивают модели и гарантии, а решение «добавить Cassandra рядом с PostgreSQL» в реальной команде упирается в деньги. Их считают по трём статьям, и ни одна из них не про лицензию.
Железо. У распределённой базы нет конфигурации «один сервер». Cassandra с фактором репликации три это минимум три узла, а с запасом под выход одного из строя и уплотнение, которому нужно свободное место, пять или шесть; каждому нужны быстрые диски и память под кэш. MongoDB это три узла на реплику, а шардированный кластер начинается с одиннадцати. Elasticsearch держит копию данных в индексе плюс реплику, и его узлы упираются в память раньше, чем в диск. Для сравнения PostgreSQL с одной репликой это два сервера, и они уже есть.
Часы людей. Это самая большая и самая невидимая статья. Резервные копии и проверка восстановления, обновление версий, мониторинг и дежурство по ночам, ремонт после сбоя, а для Cassandra ещё и регулярный repair, без которого воскресают удалённые данные. Вторая модель данных в голове у каждого разработчика и второй набор грабель. Оценка снизу: четверть ставки инженера на каждое хранилище, которое живёт в проде, и это при том, что он его уже знает.
Лицензии и облачные наценки. Здесь ситуация пёстрая, и её проверяют перед выбором, а не после. Oracle лицензируется по ядрам и стоит как отдел. Neo4j, Redis и Elasticsearch дают бесплатную редакцию с урезанными кластером и резервным копированием, а платную по узлам. Управляемый сервис в облаке снимает часть «часов людей», но берёт за это в полтора-три раза больше, чем те же виртуальные машины, и в нём нельзя поправить то, что провайдер не вынес в настройки.
Считают так: цена железа за год плюс доля ставки инженера плюс лицензия или наценка, и это сравнивают не с нулём, а с ценой решения той же задачи в базе, которая уже есть: секционированием, jsonb, pg_trgm, репликой под аналитику. Если разница не в разы, вторая база не окупится. Если задача в первой базе не решается вовсе (запись из трёх дата-центров, миллиарды строк аналитики), считать нечего, и тогда цифры нужны, чтобы выбрать между своей установкой и управляемым сервисом.
Коротко
- Cassandra — колоночная распределённая NoSQL без главного узла; её конёк — линейная масштабируемость записи и постоянная доступность, в том числе в нескольких дата-центрах.
- В отличие от PostgreSQL, у неё нет полноценных транзакций,
JOINи произвольных запросов; согласованность по умолчанию итоговая. - В отличие от MongoDB, у неё нет первичного узла и она сильнее в потоке записи, но жёстче в модели и запросах.
- Таблицы Cassandra проектируют от запросов, сознательно дублируя данные.
- Ниша Cassandra узкая: телеметрия, события, логи, счётчики — большой поток записи и предсказуемые обращения по ключу. Для связей, транзакций и аналитики берут PostgreSQL.
- Вторая база стоит железа (три узла и больше), четверти ставки инженера и лицензии или облачной наценки; сравнивают с решением в уже существующей базе, а не с нулём.
- Владение Cassandra — это минимум три узла, плановая сверка реплик, забота об уплотнении с запасом места, настройка виртуальной машины Java и почти всегда вторая база под аналитику.
- Порог входа — три условия одновременно: запись не влезает в одну машину (это десятки тысяч в секунду, а не тысячи), данные растут безгранично, запросы известны заранее.
- В той же нише стоят ScyllaDB (совместима, без Java), DynamoDB и YDB; шардирование MongoDB не избавляет от выбора ключа — те же грабли перекоса.
Что почитать дальше
- Cassandra: модель данных — почему таблицы проектируют от запросов и что нельзя в
WHERE. - Согласованность и репликация Cassandra — фактор репликации, кворумы и повторы из Java.
- PostgreSQL или MongoDB — соседняя развилка про документную модель и jsonb.