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

Ключ к пониманию Cassandra — её топология. В PostgreSQL и MongoDB есть первичный узел, через который идёт запись. В Cassandra такого узла нет: все узлы кластера равноправны, и запись принимает любой из них. Именно из этого растут и её сильные стороны (масштаб записи, отсутствие единой точки отказа), и её ограничения. Разберём, как это устроено.

Кольцо и партиционирование по токенам

Узлы Cassandra образуют логическое кольцо. Всё пространство возможных значений хеша делится на диапазоны, и каждый узел отвечает за свой участок кольца.

Когда вы пишете строку, Cassandra берёт её ключ раздела (partition key), прогоняет через хеш-функцию (партиционер, обычно Murmur3) и получает число — токен. Токен указывает точку на кольце, а значит — узел, который отвечает за этот диапазон. Так по ключу раздела детерминированно определяется, где живут данные. Это принцип консистентного хеширования: при добавлении или удалении узла перераспределяется лишь небольшая часть данных, а не всё кольцо.

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

Gossip: как узлы знают друг о друге

Раз главного узла нет, кто хранит карту кластера — кто жив, кто отвечает за какие диапазоны? Никто централизованно. Узлы обмениваются состоянием через протокол gossip: раз в секунду каждый узел «сплетничает» с несколькими случайными соседями, передавая, что он знает о состоянии всех остальных. Через несколько раундов информация о новом или упавшем узле расходится по всему кластеру. Так кластер обходится без диспетчера — знание о топологии распределено, как и данные.

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

Путь записи: почему запись такая быстрая

Высокая скорость записи Cassandra — не магия, а следствие устройства пути записи. Когда координатор доставил запись на узел-реплику, происходит два действия одновременно:

  1. запись дописывается в commit log — журнал на диске, только добавление в конец (последовательная запись, поэтому быстрая); он нужен для долговечности — по нему восстановят данные после сбоя;
  2. запись кладётся в memtable — структуру в оперативной памяти.

На этом запись считается выполненной — на диск в структуру данных ничего искать и перезаписывать не пришлось. Отсюда и скорость: никаких обновлений на месте. Когда memtable наполняется, её целиком сбрасывают на диск в новый файл — SSTable (Sorted String Table). SSTable неизменяем: его больше никогда не правят, только читают.

Важное следствие: обновление и удаление — это тоже записи, а не правки. Обновили строку — записали новую версию в свежий SSTable. Удалили строку — записали специальную пометку «удалено», её называют tombstone (надгробие). Старые версии и tombstone какое-то время сосуществуют в разных SSTable.

Compaction и tombstones

Раз данные только дописываются, со временем накапливается много SSTable, где одна и та же строка встречается в разных версиях. Чтобы чтение не замедлялось и место не расходовалось зря, работает фоновый процесс compaction (уплотнение): он сливает несколько SSTable в один, оставляя только актуальную версию каждой строки и выкидывая перекрытые.

Здесь же окончательно применяются tombstone: удалённые данные физически исчезают только после compaction, и не сразу — tombstone держат заданный срок (grace period, по умолчанию 10 дней), чтобы удаление успело разойтись по всем репликам. Отсюда классическая грабля: массовые удаления создают массу tombstone, а они замедляют чтение (движку приходится пропускать «надгробия»), пока compaction их не уберёт. Cassandra не любит, когда её используют как очередь с постоянным «положил-удалил».

Чтение при таком устройстве — это слияние: движок собирает нужную строку из memtable и нескольких SSTable, выбирая самую свежую версию. Ускоряют это вспомогательные структуры (Bloom-фильтры, индексы разделов), чтобы не заглядывать в SSTable, где искомой строки заведомо нет.

Репликация и несколько дата-центров

Данные каждой партиции хранятся не на одном узле, а на нескольких — их число задаёт replication factor (RF). RF = 3 означает три копии на трёх разных узлах кольца. Это основа и надёжности (упал узел — данные есть на других), и доступности (запрос обслужит любая живая реплика). Подробно про то, как из RF и уровней согласованности складываются гарантии, — в статье про согласованность и репликацию.

Отдельная сила Cassandra — несколько дата-центров. С правильной стратегией репликации (NetworkTopologyStrategy) копии раскладывают по нескольким ЦОД, и кластер работает в режиме «активный-активный»: каждый ЦОД принимает и чтение, и запись локально, а данные асинхронно расходятся между ЦОД. Приложение читает и пишет в ближайший ЦОД с низкой задержкой, а падение целого ЦОД не останавливает сервис. Именно поэтому Cassandra выбирают для геораспределённых систем, которые должны быть доступны всегда.

Коротко

  • В Cassandra нет главного узла: узлы образуют кольцо, запись принимает любой (становясь координатором), а данные распределены по токену ключа раздела (консистентное хеширование, vnodes).
  • Топологию узлы знают через gossip — обмен состоянием со случайными соседями, без центрального диспетчера.
  • Путь записи — commit log (долговечность) + memtable (память) → сброс в неизменяемый SSTable. Никаких правок на месте, поэтому запись быстрая; обновление и удаление — это новые записи (удаление — tombstone).
  • Compaction сливает SSTable, оставляя актуальные версии и убирая tombstone; массовые удаления плодят tombstone и замедляют чтение.
  • Replication factor задаёт число копий; NetworkTopologyStrategy раскладывает их по нескольким ЦОД в режиме активный-активный — отсюда геораспределённость и постоянная доступность.

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

  • Модель данных Cassandra — как проектировать таблицы под это устройство: partition key, clustering columns, проектирование от запросов.
  • Согласованность и репликация — уровни согласованности и как получить строгое чтение поверх настраиваемой модели.
  • Cassandra, PostgreSQL или MongoDB — когда этот масштаб оправдан, а когда избыточен.