Apache Cassandra

Cassandra — распределённая NoSQL-база с линейной масштабируемостью: петабайты данных, отсутствие единой точки отказа, масштабирование добавлением узлов.

Уровень: средний 18 разделов ⏱ 3 часа

1. Что такое Cassandra

Cassandra построена на архитектуре ring: каждый узел хранит часть данных и равен другим. Запросы обслуживает любая реплика, поэтому нет единой точки отказа (в отличие от MySQL-replica-паттернов).

Модель хранения — «широкая колонка» (column-family): строки по ключам, колонки по времени.

2. Установка

docker run -d --name cass -p 9042:9042 cassandra:4
docker exec -it cass cqlsh

Консоль cqlsh — язык CQL (похож на SQL, но со своей логикой ключей).

3. Ключи: partition vs clustering

CREATE KEYSPACE shop
WITH replication = {'class': 'SimpleStrategy',
                    'replication_factor': 1};

CREATE TABLE shop.users (
    user_id UUID,
    email   text,
    name    text,
    PRIMARY KEY (user_id)
);

CREATE TABLE shop.messages (
    user_id  UUID,
    msg_time timestamp,
    body     text,
    PRIMARY KEY (user_id, msg_time)
) WITH CLUSTERING ORDER BY (msg_time DESC);

4. Чтение и запись: CRUD

INSERT INTO shop.users (user_id, email, name)
VALUES (uuid(), 'a@example.com', 'Аня');

SELECT * FROM shop.users
WHERE user_id = 11111111-aaaa-0000-0000-000000000000;

UPDATE shop.users SET name = 'Anna' WHERE user_id = ...;
DELETE FROM shop.users WHERE user_id = ...;
WHERE в Cassandra всегда начинается с partition key (и дополнительно clustering key) — полнотекстовых и широких сканирований не существует.

5. Репликация и стратегии

Параметр replication_factor — сколько копий данных хранить. Уровни согласованности: ONE (быстро), QUORUM (надёжно), ALL.

6. Моделирование под запросы

Cassandra не дружит с JOIN'ами и ad-hoc запросами — моделируй таблицы под конкретные обращения (query-first design):

-- таблица под запрос «последние сообщения пользователя»
CREATE TABLE shop.feeds (
    user_id UUID,
    time_id timeuuid,
    text    text,
    PRIMARY KEY (user_id, time_id)
) WITH CLUSTERING ORDER BY (time_id DESC);

Дублируй данные в разные таблицы осознанно — это норма Cassandra.

7. Materialized views и secondary indexes

Индексы по неключевым колонкам и материализованные представления решают часть задач фильтрации. Пользуйся ими аккуратно: на больших партициях они медленны, лучше вторая таблица.

8. Время жизни (TTL)

INSERT INTO shop.sessions (session_id, data)
VALUES (..., 'payload')
USING TTL 3600;   # удалится через час

9. Кластер и узлы

10. Управляемый сервис и драйверы

11. Обработка запросов и модель согласованности

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

УровеньЗаписьЧтениеСценарий
QUORUMбольше половины узловсверка на большинствебазовые операции с сильной гарантией
ONEодин узелмгновенновысоконагруженный лог, телеметрия
LOCAL_ONEодин узел в датацентрелокальнораспределённый кластер по DC
ALLвсе узлывсе узлыкритичные данные (замедляет)
cqlsh> CONSISTENCY QUORUM;
cqlsh> SELECT * FROM shop.feeds WHERE user_id = ...;
Если пишешь с уровнем ALL, а один узел лежит — запись не пройдёт. Выбирайте QUORUM (или LOCAL_QUORUM при мульти-DC), иначе каждая замена узла валит приложение.

12. Data modeling: практические принципы

Модель данных в Cassandra строят «от запроса»: сначала формулируют все паттерны чтения, потом проектируют таблицы под них. Ключевые правила:

  1. partition key распределяет данные по узлам;
  2. clustering columns определяют порядок внутри партиции;
  3. один запрос — одна таблица: денормализация это норма;
  4. тенди номер: минимум партиций-«горячих», идеально ~100 МБ на партицию.
-- модель «профиль пользователя» под запрос по id
CREATE TABLE shop.users (
    user_id UUID PRIMARY KEY,
    name    text,
    email   text
);

-- под запрос «все заказы пользователя по убыванию даты»
CREATE TABLE shop.orders_by_user (
    user_id UUID,
    order_ts timeuuid,
    amount  decimal,
    PRIMARY KEY (user_id, order_ts)
) WITH CLUSTERING ORDER BY (order_ts DESC);
Главный вопрос моделирования: «по какому ключу я прочитаю данные?». Если ключ не совпадает с запросом — добавите вторичную таблицу и дублируйте данные осознанно.

13. Tombstones, гарантии и удаление

Удаление в Cassandra не мгновенно: строки помечаются tombstone, затем garbage collection (GC) убирает их через период на основе gc_grace_seconds. Много tombstone'ов замедляют чтения.

-- удалить строку
DELETE FROM shop.feeds WHERE user_id = ... AND time_id = ...;

-- TTL выставляет tombstone автоматически
INSERT INTO shop.sessions (session_id, data)
VALUES (..., 'x') USING TTL 3600;
Tombstone это не стирание, а пометка: пока все реплики не согласованы, «удалённая» строка всё ещё физически живёт. Проверяйте через nodetool compact при серьёзных чистках.

14. Мониторинг и производительность

Cassandra в проде требует наблюдения: нагрузка, задержки, размеры партиций и здоровье узлов. Стандартные команды — ваши первые глаза.

nodetool status              # состояние кластера
nodetool ring                # распределение токенов
nodetool tpstats             # потоки: заблокированные операции
nodetool compactionstats     # фоновая компакция
nodetool tablestats keyspace # статистика таблиц
МетрикаЧто смотреть
p99 read/write latencyзадержки не должны взлетать при росте
commit logне переполнен (flush быстрее пишется)
сжатие и компакцияпроценты и длительность операций
память (offheap, JVM)частично вокруг heap 1–4 ГБ
SWISS-правило: если p99 вдвое выше p50, ищите «горячие» ключи или перекос партиций. Перебалансируйте ключи и пересмотрите модель.

15. Lightweight Transactions (LWT)

Cassandra — распределённая база без мгновенной глобальной консистентности: обычный INSERT просто перезапишет данные при конкурентных записях. Для операций «проверь и запиши» используется LWT.

-- создать, только если ключ не существует
INSERT INTO users (id, email) VALUES ('u1', 'a@b.com')
IF NOT EXISTS;

-- atomic conditional update
UPDATE accounts SET balance = balance - 100
WHERE id = 'u1' IF balance >= 100;
Не злоупотребляйте LWT на «горячих» ключах: стоимость Paxos растёт с параллелизмом. Для инкремента чаще подходят напряжения через counter (раздел 16).

16. Counter-таблицы

Счётчики — примитив «увеличить на N» без чтения и без конфликтов: каунтер работает через алгоритм, не теряющий операции при репликации. Это идеально для статистики: просмотры, лайки, эквайринг-счётчики.

CREATE TABLE views (
  page_id text PRIMARY KEY,
  count counter
);

UPDATE views SET count = count + 1 WHERE page_id = 'home';
-- ответ можно прочитать позже
Если нужен счётчик с «удалением и ограничением» — переходите на обыкновенный UPDATE с LWT или агрегируйте в паре с временны́ми окнами.

17. Компакция и обслуживание

SSTable-файлы копятся, читатели платят за merge-эффект. Компакция — фоновая пересборка таблиц в более крупные: она освобождает место, ускоряет чтение, но жрёт CPU и диск.

СтратегияКогда выбрать
SizeTiered (STCS)классика для больших таблиц
Leveled (LCS)постоянные небольшие SSTable, мало tombstones
TimeWindow (TWCS)временны́е ряды с TTL
Хаотическая major compaction на больших кластерах перегружает диск. Разрешайте её в окно низкой нагрузки и только после re-estimate.

18. Безопасность: роли и TLS

По умолчанию Cassandra открыта: любой клиент может читать и писать. Для прода настройте авторизацию (пароль/роли) и шифрование канала (TLS) между клиентами и нодами.

# Cassandra.yaml
authenticator: PasswordAuthenticator
authorizer: CassandraAuthorizer
client_encryption_options:
  enabled: true
  keystore: conf/.keystore
  truststore: conf/.truststore

# создать пользователя и права
CREATE ROLE analyst WITH PASSWORD = 'secret' LOGIN;
GRANT SELECT ON keyspace1.table1 TO analyst;
Безопасность — слой поверх резервирования: сначала бэкапы, потом роли, потом TLS. Начните с авторизации и восстановления.

19. Время жизни данных (TTL) на практике

TTL — время жизни строки: данные автоматически исчезают через заданный срок. Это изящное решение для сессий, логов и временных меток, но с нюансами.

-- вставить строку, которая живёт сутки
INSERT INTO sessions (id, data) VALUES (uuid(), 'payload') USING TTL 86400;

-- обновление с новым TTL
UPDATE sessions USING TTL 3600 SET data = 'new' WHERE id = ...
Для «горящих» сессий предпочитайте отдельную таблицу с TTL: она компактируется сама и не трогает основные данные.
Массовое истечение TTL порождает расплод tombstones: следите за размером диапазона партиций.

20. Антипаттерны моделирования данных

Cassandra не любит запросы «по индексам как в SQL»: частые антипаттерны ломают производительность. Их проще избежать, если знать их заранее.

Планируйте модель сразу под конкретные запросы чтения: denormalize и дублируйте осознанно.
Lightweight transactions (раздел 15) решают счётчики и проверки, но не заменяют грамотную модель.

21. Boost производительности: настройка ключевых параметров

Исправная модель — полдела. Вторая половина — настройки: memtable, кэш, партиции и сеть. Оптимизация начинается с конфигурации, а не с покупки новых нод.

nodetool tpstats      # потоки и очереди
nodetool tablehistograms ks.tbl  # метрики чтения/записи
Следите за pending tasks: их рост — сигнал увеличить потоки или разгрузить ноды.
Меняйте конфигурацию на тестовом кластере: одна настройка может «выбить» прод на пиках.

22. Миграция данных и перенос кластера

Переезд между кластерами — частая задача: смена провайдера, версии или структуры. План миграции бережёт время и данные.

  1. сначала прогоните полный snapshot и резервную копию уникальных ключей;
  2. переносите данные по таблицам: COPY для малых, sstable2sstable для больших;
  3. перенос схемы: describe + restore keyspace;
  4. переключение клиентов: приложений по очереди, следите за лагом репликации;
  5. оставляйте старый кластер читаемым до стабильного переключения.
Перед миграцией «заморозьте» запись на старом кластере в согласованный момент времени.
Разные версии Cassandra общаются не всегда: обновляйте версии на тесте до боевого переноса.

Что дальше

Сравни схемы данных с MongoDB и Redis. Для очередей событий — Kafka.

← Вернуться к курсам