Шесть предыдущих статей серии разбирали модель данных, архитектуру, compaction, консистентность, топологию и драйверы — всё это живёт на кластере, который кто-то должен эксплуатировать: смотреть метрики, снимать backup, отвечать на вопрос «а если завтра нужно мигрировать с DynamoDB». Здесь — три эксплуатационных среза поверх того же трёхузлового кластера серии, и итоговая карта выбора, собранная не из маркетинга, а из чисел шести предыдущих статей. Заодно — то, что нельзя игнорировать при попытке воспроизвести любое из этих чисел на другом релизе: ScyllaDB с 2024 года распространяется не под классической open-source лицензией.
Это седьмая, заключительная статья серии «ScyllaDB: глубокое погружение» — после статьи о моделировании данных, статьи об архитектуре shard-per-core, статьи о compaction, tombstones и repair, статьи о consistency levels и LWT, статьи о token ring, tablets и multi-DC и статьи о shard-aware драйверах.
Стенд этой статьи — тот же основной трёхузловой кластер scylla1/2/3 (keyspace telemetry, 672 000 строк readings из первой статьи), не пересозданный ни разу за все три эксплуатационных демо: мониторинг подключается сервисом Prometheus к уже существующей сети кластера, backup/restore работает с отдельной scratch-таблицей, а DynamoDB-совместимый доступ поднят на отдельном транзитном узле. Код — в публичном репозитории примеров (digital-cookbook, каталог scylladb/ops-stand и scylladb/ops).
В статье
- Мониторинг: что реально видно в
:9180/metrics - Backup/restore: snapshot → truncate → refresh
- Alternator: DynamoDB-совместимый API
- Карта выбора: Scylla vs Cassandra vs DynamoDB vs Postgres vs ClickHouse
- Лицензия: source-available, не классический open source
- Итог серии
Мониторинг: что реально видно в :9180/metrics
ScyllaDB отдаёт Prometheus-совместимый эндпоинт :9180/metrics на каждом узле без дополнительного экспортёра — сервис prometheus (образ prom/prometheus:v3.7.3) добавляется к уже работающему кластеру одной compose-командой, не пересоздавая ни один из трёх узлов. Число scylla_*-метрик, отданных каждым узлом на момент снятия:
scylla1: 4360 scylla_* metrics
scylla2: 4352 scylla_* metrics
scylla3: 4100 scylla_* metricsТри узла с идентичной ролью в кластере отдают заметно разное число метрик — ожидаемо: часть счётчиков заводится лениво, по факту события (конкретный тип compaction, конкретный consistency level, конкретная таблица), а не декларируется заранее фиксированным списком на старте процесса. Кластер, который за время серии прогнал через себя compaction, LWT, multi-DC-сценарии и shard-aware бенчмарки на разных таблицах, неравномерно «нарастил» набор метрик на разных узлах — это следствие истории нагрузки, а не расхождение конфигурации.
Ключевые значения на scylla1 (накоплено за всю серию, кластер не пересоздавался между стендами):
| Метрика | Значение |
|---|---|
scylla_database_total_reads (сумма по всем class/shard) |
880862 |
scylla_database_total_writes (сумма) |
297740 |
scylla_compaction_manager_backlog (shard 0/1) |
0.0 |
scylla_storage_proxy_coordinator_cas_total_operations (сумма) |
3820 |
scylla_compaction_manager_backlog = 0.0 не значит, что метрика не работает — это легитимный нуль idle-кластера: между эпизодами активной записи compaction успевает догнать всё, что накопилось, а статья про compaction уже показала, что тот же backlog становится ненулевым именно под активной записью TWCS-окон. cas_total_operations = 3820 — та же метрика, что статья про LWT разобрала на именованном сценарии: там живой прогон дал точную дельту 9993 CAS-раундов на изолированной таблице; накопленное здесь число — это остаток того же счётчика на этом узле, прошедшем через всю серию, а не новое отдельное измерение.
Честная оговорка, актуальная для всех эксплуатационных запросов к кластеру, а не только к мониторингу: :9180/metrics, как и REST-порт :10000, слушает на сетевом адресе контейнера, а не на 127.0.0.1 — запрос curl localhost:9180/metrics изнутри самого контейнера узла возвращает connection refused, тот же запрос на IP или DNS-имя узла (scylla1:9180) с любого контейнера сети compose отвечает 200 OK. Этот паттерн уже встречался в статьях про архитектуру и compaction — здесь он же держит любой внешний Prometheus, Go- или Java-клиент, читающий метрики без доступа к docker-сокету хоста.
Backup/restore: snapshot → truncate → refresh
Backup/restore демонстрируется не на readings, а на отдельной scratch-таблице ops_backup_demo — вся демонстрация создаёт и удаляет её сама, ни разу не касаясь основного датасета серии. Цикл: 50 строк загружено → nodetool flush → nodetool snapshot → TRUNCATE (симуляция потери данных) → копирование sstable снапшота в upload/ той же табличной директории → nodetool refresh → проверка count.
ops_backup_demo: загружено, count=50
snapshot files: 245
snapshot size: 1.1M
после TRUNCATE: count=0
после nodetool refresh (из upload/): count=50
telemetry.readings count=672000 (неизменно за весь цикл)245 файлов и 1.1M на 50 строк — не диспропорция, а нормальная цена snapshot на уровне файловой системы: nodetool snapshot создаёт hard-link на каждый sstable-компонент таблицы (данные, индекс, статистику, фильтр Блума и так далее отдельными файлами), а не один архив, поэтому даже маленькая таблица даёт заметное число файлов снапшота. Ключевой результат — не абсолютные числа, а то, что nodetool refresh вернул ровно исходные 50 строк после TRUNCATE, и что readings (672 000 строк) не сдвинулась ни на одну строку за весь цикл — backup/restore демонстрационной таблицы физически изолирован от основного датасета.
Механизм restore здесь стоит назвать честно: nodetool refresh — штатный путь «загрузить sstable из upload/ без рестарта узла», рабочий и документированный для восстановления таблицы на том же узле, где был снят snapshot. Это не то же самое, что sstableloader — отдельный инструмент для переноса sstable между узлами или кластерами через полноценный streaming-протокол; в этой демонстрации восстановление выполняется на месте, sstableloader не задействован и не проверялся.
Живая находка, специфичная для этого стенда: DROP TABLE не освобождает директорию данных немедленно. После нескольких прогонов демонстрации на диске узла одновременно лежат несколько каталогов ops_backup_demo-<cf_id> от разных запусков — ScyllaDB не гарантирует синхронную с DDL физическую очистку директории удалённой таблицы, она уходит по внутреннему расписанию GC/compaction, а не в момент выполнения DROP TABLE. Наивный способ найти нужную директорию — ls data/telemetry/ | grep ops_backup_demo- — на кластере с историей нескольких прогонов совпадёт сразу с несколькими каталогами и сломает путь к актуальным данным. Рабочий способ — определить cf_id авторитетно, через system_schema.tables.id живой (не удалённой) таблицы, и адресоваться к директории по этому идентификатору напрямую, не полагаясь на перечисление файловой системы.
Честная рамка для раздела с названием «эксплуатация»: показанный цикл snapshot → restore на том же узле — это ядро механики, но не полная эксплуатационная история backup/DR. За рамками этой демонстрации сознательно осталось то, что в проде обязательно и здесь не покрыто: restore в другой/новый кластер (через sstableloader или Scylla Manager restore, а не nodetool refresh на месте), offsite/оффхостовое хранение снапшотов и регулярные restore-drills, инкрементальные бэкапы и оркестрация через Scylla Manager либо managed-бэкап ScyllaDB Cloud, а также смежные эксплуатационные слои — шифрование, TLS и аутентификация/RBAC, rolling upgrades и capacity planning. Эта статья показывает воспроизводимый минимум механики snapshot/refresh и её изоляцию от основного датасета, а не готовый DR-регламент; каждый из перечисленных слоёв — тема отдельного разбора.
Alternator: DynamoDB-совместимый API
Alternator — DynamoDB-совместимый протокол, встроенный в сам сервер ScyllaDB: тот же движок, что отвечает по CQL на порту 9042, параллельно умеет отвечать по HTTP на DynamoDB-совместимом API, если узел запущен с флагом --alternator-port. Флаг задаётся только при старте процесса, не переключается на лету, а правка command в compose для уже работающего кластера означала бы пересоздание контейнеров scylla1/2/3 и, как следствие, потерю 672 000 строк readings — в этой серии такой цены никто не платит. Поэтому Alternator демонстрируется на отдельном, транзитном, одноузловом контейнере на той же сети кластера, поднятом и снесённом самой демонстрацией:
docker run -d --name scylla-alt --network scylla-cookbook-net \
scylladb/scylla:2026.2.0 \
--alternator-port 8000 --alternator-write-isolation always \
--smp 1 --memory 2G --overprovisioned 1 --api-address 0.0.0.0Сценарий работает через AWS SDK for Go v2 (service/dynamodb) с кастомным BaseEndpoint, указывающим на scylla-alt:8000, и dummy-credentials — Alternator не проверяет AWS-подпись содержательно, но сам SDK требует непустые значения для их формирования. PutItem записывает значение, GetItem читает его обратно:
PutItem: device_id=dev-alternator-0001 value=42.50 — OK
GetItem: device_id=dev-alternator-0001 value=42.5
RESULT scenario=alternator put_ok=true get_ok=true match=truematch=true подтверждает главное: тот же процесс ScyllaDB, что обслуживает CQL-нагрузку всей серии, отвечает по DynamoDB-совместимому протоколу без отдельного прокси или транслятора. Но именно это демо и поймало живую находку про сам протокол: записанное значение "42.50" при чтении вернулось как "42.5". Это не баг Alternator и не расхождение с DynamoDB — DynamoDB-тип N хранится как decimal-значение, а не байт-в-байт строка литерала, и убирает незначащий нуль при сериализации ответа; такое поведение задокументировано у самого протокола DynamoDB и одинаково у AWS-оригинала и у Alternator. Поэтому ассерт сценария сравнивает не строки, а числовое значение (ParseFloat + равенство) — побайтовое сравнение строк было бы нечестной проверкой протокола, который сам не обещает сохранить формат литерала.
Карта выбора: Scylla vs Cassandra vs DynamoDB vs Postgres vs ClickHouse
Все шесть предыдущих статей вместе складываются в одну ось выбора — не бенчмарк «кто быстрее», а карта, по какому критерию тянуться к какому инструменту.
нужен выбор хранилища"] --> Q1{"Основной паттерн —
GROUP BY/агрегаты по всей таблице?"} Q1 -->|"Да"| CH["ClickHouse
колоночный OLAP"] Q1 -->|"Нет"| Q2{"Нужны multi-row ACID
транзакции между таблицами?"} Q2 -->|"Да"| PG["PostgreSQL
MVCC-транзакции"] Q2 -->|"Нет"| Q3{"Нужна совместимость
с DynamoDB API?"} Q3 -->|"Да"| DYN["ScyllaDB + Alternator
или managed DynamoDB"] Q3 -->|"Нет"| Q4{"Критичны предсказуемые
хвостовые латентности
без GC-пауз?"} Q4 -->|"Да"| SCY["ScyllaDB
shard-per-core, C++/Seastar"] Q4 -->|"Не критично,
JVM-экосистема ОК"| CASS["Cassandra
тот же CQL и модель данных"] style CH fill:#f4d9c6,stroke:#c67a4a style PG fill:#f4d9c6,stroke:#c67a4a style DYN fill:#e8c9a0,stroke:#c67a4a style SCY fill:#c9e4c5,stroke:#5b8a5e style CASS fill:#d9d2c0,stroke:#8a8375
flowchart TD
Q0["Партиционируемая нагрузка,
нужен выбор хранилища"] --> Q1{"Основной паттерн —
GROUP BY/агрегаты по всей таблице?"}
Q1 -->|"Да"| CH["ClickHouse
колоночный OLAP"]
Q1 -->|"Нет"| Q2{"Нужны multi-row ACID
транзакции между таблицами?"}
Q2 -->|"Да"| PG["PostgreSQL
MVCC-транзакции"]
Q2 -->|"Нет"| Q3{"Нужна совместимость
с DynamoDB API?"}
Q3 -->|"Да"| DYN["ScyllaDB + Alternator
или managed DynamoDB"]
Q3 -->|"Нет"| Q4{"Критичны предсказуемые
хвостовые латентности
без GC-пауз?"}
Q4 -->|"Да"| SCY["ScyllaDB
shard-per-core, C++/Seastar"]
Q4 -->|"Не критично,
JVM-экосистема ОК"| CASS["Cassandra
тот же CQL и модель данных"]
style CH fill:#f4d9c6,stroke:#c67a4a
style PG fill:#f4d9c6,stroke:#c67a4a
style DYN fill:#e8c9a0,stroke:#c67a4a
style SCY fill:#c9e4c5,stroke:#5b8a5e
style CASS fill:#d9d2c0,stroke:#8a8375
ScyllaDB против Cassandra — не выбор модели данных, а выбор реализации одной и той же модели. Обе говорят одним CQL-протоколом (release_version 3.0.8 на этом кластере — та же протокольная версия, что у классической Cassandra, установлено ещё в первой статье серии), обе устроены вокруг партиции, ключа кластеризации, LSM-движка, tunable consistency и LWT на Paxos. Разница — под капотом: shard-per-core на C++/Seastar без общей кучи и без JVM-style GC-пауз убирает один крупный источник хвоста латентности — глобальную GC-паузу (p99/p50 ≈ 1.09–1.13 на живом кластере; сам JVM-style stop-the-world как класс пауз в этой серии не измерялся и зависит от настройки GC — см. оговорку в статье про архитектуру), а tablets убирают ручной ребаланс токен-кольца при выводе или добавлении узла. Cassandra остаётся разумным выбором там, где JVM-экосистема (тот же runtime, что и остальной стек компании, привычная тулинг-обвязка) перевешивает выигрыш в хвостовой латентности; ScyllaDB — там, где именно предсказуемость хвоста и есть требование, а не приятный бонус.
ScyllaDB против DynamoDB (managed). Alternator доказал этим стендом не гипотезу, а факт: match=true на реальном PutItem/GetItem через AWS SDK — тот же движок ScyllaDB отвечает DynamoDB-совместимым API без прокси-слоя. Оговорка идёт первой, до соблазна упростить: Alternator реализует совместимое подмножество DynamoDB API — не весь протокол байт-в-байт, — и доказано это здесь только на уровне smoke-теста PutItem/GetItem. Перед реальной миграцией нужно проверить конкретные паттерны доступа приложения и то, как в вашей версии поддержаны нужные возможности: модель согласованности, управление ёмкостью (capacity/throughput), TTL, streams, global tables, вторичные индексы. И вот при этом условии — для того подмножества, что действительно поддержано и проверено, — миграция в обе стороны (от DynamoDB к self-hosted ScyllaDB и обратно) в идеальном случае сводится к смене endpoint в конфигурации клиента, а не к переписыванию слоя доступа к данным. То есть «смена endpoint» — правильная интуиция про порядок величины усилий, а не обещание бесшовной миграции.
Но совместимость API не отменяет операционной ответственности: self-hosted Alternator означает, что backup/restore, repair, мониторинг — всё, что разобрано в этой статье и в серии — остаётся на стороне того, кто эксплуатирует кластер, а не спрятано за консолью управляемого сервиса. Для тех, кому такая ответственность нежелательна, у ScyllaDB есть managed-предложение — ScyllaDB Cloud, снимающее ровно тот операционный слой, что здесь показан вручную (мониторинг, backup, repair, обновления), ценой отказа от части инфраструктурного контроля. Путь миграции без переписывания кода — главный практический довод в пользу Alternator, не производительность как таковая.
ScyllaDB/Cassandra-семья против Postgres. Разная модель согласованности — tunable consistency level плюс LWT-Paxos на уровне одной партиции против MVCC-транзакций с произвольным числом таблиц (сравнение моделей — в статье про транзакции в KV, документных и wide-column хранилищах, где та же LWT на ScyllaDB уже разбиралась под другим углом — что она гарантирует, а не чем стоит) — и разная модель данных: партиция первична, JOIN нет вовсе, денормализация — осознанный приём, а не компромисс (см. первую статью серии). Выбор Scylla оправдан там, где нагрузка write-heavy и паттерн доступа предсказуем по ключу партиции — телеметрия этой серии типичный случай; там, где нужны произвольные JOIN и многотабличные инварианты, реляционная модель ближе к задаче.
ScyllaDB/Cassandra-семья против ClickHouse. Профили ортогональны, а не конкурентны: Scylla — точечный и диапазонный доступ по известному ключу партиции (частые point-read/point-write, ровно то, что измеряла вся эта серия), ClickHouse — колоночный OLAP с агрегациями и сканами по столбцам, чему посвящена отдельная серия про ClickHouse. Телеметрия, использованная во всех стендах этой серии, на практике часто живёт в обеих системах одновременно: горячий операционный слой (последние показания, алерты, точечные запросы устройства) — в ScyllaDB, холодный аналитический слой (агрегаты за период, произвольные срезы) — в ClickHouse. Это не взаимоисключающий выбор, а разделение по паттерну доступа.
Лицензия: source-available, не классический open source
Отдельная оговорка, которую стоит вынести из сноски в отдельный раздел: с 2024 года ScyllaDB перешла на годовые unified-релизы с моделью распространения ScyllaDB Source Available License — это не классическая open-source лицензия предыдущих мажорных версий проекта. Весь стенд серии пинуется к конкретному образу scylladb/scylla:2026.2.0, актуальному на момент написания. Условия лицензирования и сама схема версионирования (YYYY.N) способны отличаться в будущих релизах — при воспроизведении любого стенда этой серии на другом образе поведение, особенно специфика tablets, Alternator и состав метрик, стоит сверять по официальным release notes соответствующего года, а не считать эту серию универсально применимой ко всем версиям ScyllaDB без исключения.
Итог серии
Семь стендов одного и того же живого кластера прошли путь от выбора ключа партиции до эксплуатации: почему ключ партиции решает всё ещё до архитектуры, как shard-per-core убирает GC-паузы из хвоста латентности, как TWCS, tombstones и repair держат LSM-движок в порядке, что реально стоит кворум и во сколько раз дороже Paxos-консенсус LWT, как tablets заменили token ring и что происходит при отказе целого датацентра и сколько реально даёт шард-осведомлённая маршрутизация в Go и Java — каждая статья добавляла один слой к финальной карте выбора этой статьи, ни одна цифра в ней не взята с потолка.
Более широкий взгляд на ландшафт хранения данных, если ScyllaDB — не единственный кандидат в рассмотрении, — в хабе «Карта данных»готовится, с 22 сентября. Компромисс между консистентностью и доступностью, который tunable consistency этой серии реализует на практике, разобран в общем виде в статье про CAP и PACELCСкоро — она объясняет, почему LOCAL_QUORUM и QUORUM вообще существуют как разные настройки, а не единственный режим.
Версии на стенде: образ scylladb/scylla:2026.2.0, CQL/release_version — 3.0.8, Prometheus — prom/prometheus:v3.7.3, датасет — детерминированный генератор с -seed 42 (672 000 строк: 500 устройств × 14 суток × 96 замеров в сутки), не тронутый ни разу за все семь стендов серии.
Комментарии