Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Иллюстрация к архитектуре Apache Cassandra и NoSQL моделированию

Архитектура Apache Cassandra: устройство SSTables, Memtable и проектирование NoSQL

Глубокий разбор внутренней архитектуры распределенной СУБД Apache Cassandra. Анализируются механизмы последовательной записи через CommitLog и Memtable, сброс на диск в виде SSTables, работа фильтров Блума, а также ключевые принципы проектирования NoSQL-схем под паттерны запросов.

Архитектура Apache Cassandra: устройство SSTables, Memtable и проектирование NoSQL

Распределенная СУБД Apache Cassandra заслужила статус одного из наиболее надежных решений для работы с колоссальными объемами записанных данных под высокой нагрузкой. Кластер Cassandra строится на равноправной архитектуре Peer-to-Peer без выделенного главного узла (Master-Node), а хранение данных на каждом узле опирается на идеи структуры LSM-tree (Log-Structured Merge-tree). В отличие от реляционных баз данных (таких как PostgreSQL), использующих B-деревья с перезаписью дисковых блоков, Cassandra отдает приоритет последовательной append-only записи.

Для эффективного проектирования систем на базе Cassandra инженерам необходимо не просто выучить язык запросов CQL, но и глубоко понимать внутренний жизненный цикл данных на диске и в оперативной памяти. Понимание механики CommitLog, Memtable, SSTable и уплотнения (Compaction) предохраняет приложения от непредсказуемых просадок производительности.

Анатомия цикла записи (Write Path): от координатора до диска

При отправке запроса на запись (INSERT или UPDATE) любой узел кластера, принявший вызов, становится узлом-координатором (Coordinator Node). По хешу ключа партиции (Partition Key) координатор вычисляет реплики, ответственные за хранение строки, и перенаправляет мутацию на них.

На целевой реплике запись проходит два независимых пути:

  1. Последовательная фиксация в CommitLog: Изменение моментально записывается в конец дискового журнала CommitLog. Эта операция выполняется как линейный append-on-disk и не требует поиска секторов диска. Журнал CommitLog используется для аварийного восстановления при сбоях питания.
  2. Запись в оперативную память Memtable: Данные помещаются в структуру Memtable в RAM, где поддерживаются в отсортированном порядке по ключам.

После записи в CommitLog и Memtable узел возвращает подтверждение успешной операции со сверхнизкой задержкой, так как тяжелые произвольные дисковые операции еще не происходили.

[ Клиент ] ──> ( Узел-Координатор )
                     │
                     ▼
          ┌─────────────────────┐
          │   Реплика (Node)    │
          └──────────┬──────────┘
                     │
         ┌───────────┴───────────┐
         ▼                       ▼
 ┌───────────────┐       ┌───────────────┐
 │   CommitLog   │       │   Memtable    │
 │ (Диск/Append) │       │ (RAM/Sorted)  │
 └───────────────┘       ───────┬───────┘
                                │ (Flush)
                                ▼
                         ┌───────────────┐
                         │    SSTable    │
                         │(Неизменяемый) │
                         └───────────────┘

Рождение SSTable и процесс уплотнения (Compaction)

Оперативная память Memtable имеет ограниченный размер. При достижении заданного лимита Memtable переходит в статус immutable, а на ее месте создается новая пустая структура. Неизменяемая Memtable скидывается на диск, превращаясь в SSTable (Sorted String Table). После этого соответствующий сегмент CommitLog очищается.

Файлы SSTable неизменяемы (immutable). Новые данные никогда не перезаписывают существующие файлы на диске. При операциях UPDATE или DELETE Cassandra создает новую запись с более свежим временным штампом (timestamp) или специальным маркером удаления — Tombstone.

Со временем на диске накапливается много файлов SSTable. Для предотвращения деградации чтения запускается фоновый процесс Compaction (Уплотнение):

  • Несколько старых SSTables объединяются в один новый неизменяемый файл.
  • Дубликаты разрешаются в пользу записи с самым свежим timestamp.
  • Устаревшие Tombstones физически удаляются с диска.

Чтение данных (Read Path) и роль фильтров Блума

Так как фрагменты одной строки могут находиться в разных SSTable и свежей Memtable, операция чтения проверяет несколько структур:

  • Memtable: Проверяется в первую очередь в оперативной памяти.
  • Bloom Filter (Фильтр Блума): Вероятностная структура данных в RAM для каждой SSTable. С вероятностью 100% сообщает, если ключа точно нет в данном файле SSTable, позволяя пропустить дисковое чтение.
  • Partition Index & Summary: Указывают точные смещения внутри файла SSTable для поиска ключа.
               ┌───────────────────────────────┐
               │    Запрос чтения (Read Key)   │
               └───────────────┬───────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            ▼                                     ▼
   ┌─────────────────┐                   ┌─────────────────┐
   │ Проверка RAM    │                   │   Bloom Filter  │
   │   (Memtable)    │                   │   (для SSTable) │
   └─────────────────┘                   └────────┬────────┘
                                                  │ (Ключ возможен)
                                                  ▼
                                         ┌─────────────────┐
                                         │ Partition Index │
                                         └────────┬────────┘
                                                  │
                                                  ▼
                                         ┌─────────────────┐
                                         │  Чтение SSTable │
                                         └─────────────────┘

Проектирование NoSQL-схем под паттерны запросов

Главная ошибка инженеров с реляционным бэкграундом — попытка спроектировать схему в Cassandra через нормализацию таблиц. В Cassandra нет JOIN-ов, а запрос без указания ключа партиции вызовет сканирование всех узлов кластера (Scatter-Gather anti-pattern).

1. Принцип «Одна таблица — один запрос»

Схема таблиц разрабатывается строго под готовые запросы приложения. Если данные нужно читать и по user_id, и по email, в Cassandra создаются две отдельные денормализованные таблицы: users_by_id и users_by_email.

2. Выбор Partition Key и Clustering Columns

Первичный ключ таблицы в CQL состоит из двух частей:

  • Partition Key: Определяет, на какой узел попадет строка. Он должен обеспечивать равномерное распределение данных без создания «горячих» партиций (Hot Partitions).
  • Clustering Columns: Задают физический порядок сортировки строк внутри одной партиции на диске, позволяя читать диапазоны данных по времени.
CREATE TABLE user_events (
    user_id uuid,
    event_time timestamp,
    event_type text,
    payload text,
    PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

В этом примере user_id — Partition Key (все события пользователя лежат на одной ноде), а event_time — Clustering Column (события отсортированы на диске по убыванию времени).

Инженерный чек-лист по работе с Cassandra

  • Не использовать вторичные индексы (Secondary Indexes) на полях с низкой селективностью. Создавайте явные денормализованные таблицы.
  • Контролировать размер партиции. Рекомендуемый максимальный размер одной партиции — до 100 МБ или не более 100 000 строк.
  • Учитывать Lifecycle Tombstones. При частых операциях DELETE или коротком TTL отслеживайте накопление маркеров удаления, вызывающих таймауты чтения.
  • Настраивать уровень согласованности (Consistency Level). Для критичных операций задействуйте формулу LOCAL_QUORUM при записи и чтении, гарантирующую провайдерскую последовательность.

Выводы

Apache Cassandra — это мощная платформа для масштабируемых систем, способность которой обрабатывать записи опирается на механизмы append-only хранилища LSM-tree. Понимание взаимодействия Memtable, SSTable и уплотнения в сочетании с грамотным проектированием партиций позволяет строить отказоустойчивые архитектуры под высокие нагрузки.