Перейти к основному содержимому

Первые шаги с Cassandra

Разработчику Аналитику Тестировщику
Архитектору Инженеру


Установка системы Apache Cassandra

Процесс установки требует наличия среды выполнения Java (JDK), так как Cassandra написана на Java. Для работы необходим серверный компонент (cassandra) и клиентские утилиты (cqlsh).

Установка программ обычно выглядит так (сначала JDK, затем пакет Cassandra — или один корпоративный мастер):

Play ITЗагрузка интерактивного демо…

Play ITЗагрузка интерактивного демо…


Требования к системе

  • Операционная система: Linux (рекомендуется Ubuntu/CentOS), macOS или Windows.
  • Ява (JDK): Версия 8, 11 или 17 (в зависимости от версии Cassandra).
  • Права администратора на машине.
  • Минимум 2 ГБ оперативной памяти.
  • Доступ к интернету для загрузки пакетов.

Алгоритм установки

Вариант А — Установка на Linux (на примере Ubuntu/Debian)

На Ubuntu/Debian:

sudo apt update
sudo apt install openjdk-11-jdk
echo "deb http://www.apache.org/dist/cassandra/debian 41x main" | sudo tee -a /etc/apt/sources.list.d/cassandra.sources.list
curl https://www.apache.org/dist/cassandra/KEYS | sudo apt-key add -
sudo apt-get update
sudo apt-get install cassandra

Примечание: apt-key устарел; для production используйте подписанный deb822-репозиторий или Docker (вариант Б ниже). Для локального обучения команды выше достаточно.

Проверка службы:

sudo systemctl status cassandra

Интерактивная оболочка:

cqlsh

Вариант Б — Использование Docker (универсальный способ)

Создание контейнера позволяет быстро развернуть окружение без сложной настройки зависимостей:

docker run --name my-cassandra \
-d \
-p 9042:9042 \
-p 7000:7000 \
-p 7001:7001 \
-e CASSANDRA_CLUSTER_NAME='MyCluster' \
cassandra:latest

Подключение к контейнеру:

docker exec -it my-cassandra cqlsh

Создание ключевых пространств (Keyspace)

В Cassandra аналогом базы данных является keyspace (ключевое пространство). Оно определяет параметры репликации и стратегию хранения данных. В отличие от SQL, создание keyspace обязательно перед созданием таблиц.


Командный способ

В cqlsh:

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

Примечание: replication_factor — число копий данных в кластере (1 — только тест, в production обычно 3; см. блок CREATE KEYSPACE выше).


Выбор стратегии репликации

СтратегияОписаниеСценарий использования
SimpleStrategyРеплицирует данные на соседние узлы в порядке их следования.Одноразовые кластеры, тестовые среды.
NetworkTopologyStrategyПозволяет контролировать репликацию по зонам availability (AZ) и регионам.Продвинутые кластеры, требующие отказоустойчивости.

Переключение контекста на созданное пространство:

USE company_db;

Создание таблицы и первичного ключа

Таблица в Cassandra — это логическая структура, но её проектирование критически зависит от модели запросов. Данные не хранятся в произвольном порядке, а сортируются согласно Первичному ключу.


Синтаксис создания таблицы

Первичный ключ состоит из Partition Key (ключ партиции) и Clustering Columns (кластеризующие колонки).

CREATE TABLE employees (
employee_id UUID PRIMARY KEY,
first_name TEXT,
last_name TEXT,
email TEXT,
hire_date TIMESTAMP,
salary DECIMAL,
department_id INT
);

Анализ компонентов определения

  • employee_id: Тип UUID гарантирует уникальность без внешних генераторов. Это Partition Key. Все данные с одним ID будут находиться на одном узле.
  • first_name, last_name, email: Текстовые поля (TEXT).
  • hire_date: Тип TIMESTAMP для хранения даты и времени.
  • salary: Тип DECIMAL для точных финансовых вычислений.
  • department_id: Целочисленное поле.

Проектирование Первичного ключа

В Cassandra нельзя писать SELECT * WHERE column != partition_key. Запросы всегда должны начинаться с Partition Key. Сводная таблица "запрос приложения → PRIMARY KEY" — в главе Cassandra.

Пример таблицы с составным ключом для частого поиска по отделу:

CREATE TABLE employees_by_dept (
department_id INT,
employee_id UUID,
first_name TEXT,
last_name TEXT,
salary DECIMAL,
PRIMARY KEY (department_id, employee_id)
);

Здесь department_id — Partition Key, а employee_id — Clustering Column. Данные внутри отдела будут отсортированы по ID сотрудника.


Добавление ограничений и индексов

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


Ограничения уникальности

Уникальность обеспечивается только частью Первичного ключа. Если попытаться вставить дубликат employee_id, возникнет ошибка.

Для обеспечения уникальности других полей (например, email) необходимо создать отдельную таблицу-справочник:

CREATE TABLE emails_lookup (
email TEXT PRIMARY KEY,
employee_id UUID
);

Создание индексов

Вторичный индекс в CQL создаётся только по одному столбцу (составных индексов, как в SQL, нет). Индекс не заменяет partition key: запрос без partition key разносится по всем узлам кластера.

Создание индекса по фамилии:

CREATE INDEX idx_employees_last_name ON employees(last_name);

Запрос по индексу без partition key (допустимо для отладки, не для production):

SELECT * FROM employees WHERE last_name = 'Ivanov';

Важно: такой запрос выполняет fan-out на каждый узел. Для частого поиска по email или last_name лучше отдельная lookup-таблица с нужным partition key (см. emails_lookup выше), а не вторичный индекс.


Выполнение CRUD операций

CRUD в Cassandra реализован через язык CQL (Cassandra Query Language).

Что можно в WHERE:

  • обязательно — полный partition key (все его столбцы);
  • опционально — clustering columns (равенство и диапазоны, только слева направо);
  • фильтр по неключевому столбцу — только через вторичный индекс + fan-out или ALLOW FILTERING (отладка).

Подробнее: таблица проектирования.


Создание записей (Create)

Вставка нового сотрудника:

INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (uuid(), 'Ivan', 'Ivanov', 'ivanov@example.com', toTimestamp(now()), 75000.00, 1);

Вставка с явным указанием UUID:

INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'Maria', 'Petrova', 'petrova@example.com', '2024-02-10', 82000.00, 2);

Lightweight transaction (вставка только если строки с таким ключом ещё нет):

INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'Alexey', 'Sidorov', 'sidorov@example.com', toTimestamp(now()), 65000.00, 1)
IF NOT EXISTS;

Требует согласованности SERIAL / LOCAL_SERIAL — медленнее обычной вставки. С uuid() в ключе смысл IF NOT EXISTS теряется: каждая вставка создаёт новый ключ.


Чтение данных (Read)

Выборка конкретного сотрудника по ID (самый быстрый запрос):

SELECT * FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

Выборка всех сотрудников определенного отдела (требуется указание Partition Key):

SELECT * FROM employees_by_dept WHERE department_id = 1;

Сортировка результатов внутри партиции:

SELECT * FROM employees_by_dept WHERE department_id = 1 ORDER BY employee_id DESC;

Обновление данных (Update)

Изменение зарплаты сотрудника:

UPDATE employees SET salary = 80000.00 WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

Обновление с проверкой условия (lightweight transaction — условие только в IF, не в WHERE):

UPDATE employees SET salary = 85000.00
WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
IF salary < 80000;

Ответ сервера: [applied] true или false с текущими значениями столбцов из IF.


Удаление данных (Delete)

Удаление конкретного сотрудника:

DELETE FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

Удаление конкретной колонки:

DELETE email FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

Очистка всей таблицы (осторожно!):

TRUNCATE TABLE employees;

Дополнительные таблицы под другие запросы

В Cassandra нет SQL-VIEW и нет JOIN. Вместо одной "универсальной" таблицы создают несколько таблиц с разным PRIMARY KEY под разные запросы (см. таблицу проектирования).

Двухшаговый lookup по email (паттерн "запрос → ключ → основная таблица"):

INSERT INTO emails_lookup (email, employee_id)
VALUES ('ivanov@example.com', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11');

-- Шаг 1: partition key = email
SELECT employee_id FROM emails_lookup WHERE email = 'ivanov@example.com';

-- Шаг 2: partition key = employee_id
SELECT * FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';

В приложении те же два запроса выполняет код (или batch из двух prepared statements).


Materialized Views (legacy, не использовать в новых проектах)

В Cassandra 3.x были Materialized Views — автоматические копии таблицы под другим ключом. С 4.0 они deprecated, в 5.0 удалены. Причины отказа — задержки синхронизации, лишняя нагрузка на запись, сложное сопровождение.

В legacy-кластере 3.x синтаксис выглядел так:

CREATE MATERIALIZED VIEW employees_by_email AS
SELECT employee_id, first_name, last_name, email, salary
FROM employees
WHERE email IS NOT NULL AND employee_id IS NOT NULL
PRIMARY KEY (email, employee_id);

Современная замена: явная денормализованная таблица (emails_lookup + employees) и обновление в приложении или через поток событий (Kafka и т.п.).


Агрегация и эмуляция JOIN

В Cassandra отсутствуют хранимые процедуры, триггеры и операторы JOIN в классическом понимании. Логика агрегации и соединения данных реализуется на уровне приложения.


Функция с агрегатными функциями

Агрегация возможна только внутри одной партиции. Для расчета средней зарплаты в отделе используется запрос к таблице, где department_id является Partition Key.

SELECT AVG(salary) as avg_salary
FROM employees_by_dept
WHERE department_id = 1;

Результат будет вычислен на стороне сервера Cassandra, но только для данных в пределах указанной партиции.


Эмуляция JOIN через приложение

Cassandra не поддерживает JOIN между таблицами. Для получения данных из разных таблиц (например, сотрудники и отделы) необходимо выполнить два отдельных запроса и объединить результаты в коде приложения.

Шаг 1: Получение списка отделов (IN по partition key читает несколько партиций — допустимо для малого числа id):

SELECT id, name FROM departments WHERE id IN (1, 2);

Шаг 2: Получение сотрудников (отдельный запрос на каждый отдел или IN по partition key):

SELECT * FROM employees_by_dept WHERE department_id IN (1, 2);

Шаг 3: Объединение в коде (Python пример)

departments = {d['id']: d['name'] for d in dept_rows}
for emp in emp_rows:
emp['dept_name'] = departments.get(emp['department_id'])

Валидация данных (без триггеров)

Прямых триггеров в Cassandra нет. Проверка условий:

  1. На стороне приложения — перед отправкой запроса.
  2. Lightweight transactions (LWT)IF NOT EXISTS / IF col = val в INSERT/UPDATE/DELETE (медленно, только для критичных проверок).
  3. UDF (User Defined Functions) — только в Cassandra 3.x; удалены в 4.0+. В legacy-кластерах функция могла вычислять значение, но не блокировать вставку.

Пример LWT для уникальности email (предпочтительный способ):

INSERT INTO emails_lookup (email, employee_id)
VALUES ('new@example.com', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')
IF NOT EXISTS;

Условное обновление зарплаты (см. раздел Update выше):

UPDATE employees SET salary = 85000.00
WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
IF salary < 80000;

Отрицательную зарплату отсекайте в коде приложения — CQL не проверяет CHECK-ограничения, как SQL.


Сквозной практикум (15 минут)

После Docker из раздела установки или локального cqlsh:

Код ITЗагрузка примера кода…

Проверьте, что запрос всегда содержит device_id (partition key). Запрос только WHERE recorded_at > … без device_id потребует ALLOW FILTERING и не подходит для production.

Очистка:

DROP KEYSPACE IF EXISTS learning_cassandra;

Теория: Cassandra, уровни консистентности — там же.


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

Первые шаги (маршрут подборки) — Первые шаги с Redis, Первые шаги с Memcached, Первые шаги с MongoDB, Первые шаги к микросервисам, Первые шаги с SQL, Первые шаги с Docker и Kubernetes.