Всем привет! Меня зовут Александр Андреев, я инженер данных. Сегодня я хочу рассказать об одном из способов оптимизации производительности NoSQL базы данных ScyllaDB, а именно о выборе стратегии компактизации данных.

Для начала о том, что такое ScyllaDB для тех, кто впервые слышит это название.
ScyllaDB — это высокопроизводительная NoSQL база данных, созданная как улучшенная версия Apache Cassandra на C++. Она способна обрабатывать миллионы операций в секунду, что лидером среди распределенных баз данных. Но такая производительность достигается благодаря особой архитектуре хранения данных, в центре которой находится процесс компактизации данных. Эта статья посвящена рациональному выбору стратегии компактизации в ScyllaDB.
Введение: почему компактизация определяет судьбу распределенной базы данных
Представьте себе огромную библиотеку, которая получает тысячи новых книг каждый день. У вас есть выбор: потратить часы на немедленную расстановку каждой книги в идеальном алфавитном порядке или быстро сложить их в временные стопки, а затем периодически организовывать эти стопки в упорядоченные коллекции. ScyllaDB выбирает второй подход, и этот выбор лежит в основе ее феноменальной производительности.
Компактизация (compaction) — это процесс, который превращает быстрые записи, сделанные в хаотичном порядке, в упорядоченную структуру, оптимизированную для чтения. От правильного выбора стратегии компактизации зависит буквально всё: сможете ли вы использовать 90% вашего дискового пространства или только 50%, будут ли ваши запросы выполняться за миллисекунды или секунды, выдержит ли система пиковые нагрузки или рухнет под их весом.
Глубокое погружение в архитектуру хранения данных в ScyllaDB
Чтобы по-настоящему понять важность компактизации, нам нужно проследить данных в ScyllaDB от момента записи до финального чтения.
Путь данных: от клавиатуры до диска
Когда приложение отправляет запрос на запись в ScyllaDB, начинается тщательно оркестрированный процесс. Сначала данные попадают в специальную структуру в оперативной памяти, называемую Memtable. Это высокоэффективная структура данных, оптимизированная для быстрых вставок и поддержания порядка сортировки. Memtable позволяет ScyllaDB принимать сотни тысяч записей в секунду на одном сервере.
Но память не бесконечна. Когда Memtable достигает определенного размера (обычно несколько десятков мегабайт), происходит процесс, называемый "flush" — сброс на диск. Здесь рождается SSTable (Sorted String Table) — неизменяемый файл на диске, состоящий из пар ключ-значение и содержащий отсортированные данные из Memtable. Слово "неизменяемый" здесь ключевое: однажды записанный SSTable никогда не модифицируется. Неизменяемость SSTable позволяет избежать сложных проблем с конкурентным доступом и значительно упрощает репликацию данных.
Проблема накопления: когда хорошего становится слишком много
По мере работы системы количество SSTable неуклонно растет. Каждые несколько минут создается новый файл. Через день их могут быть сотни, через неделю — тысячи. И здесь начинаются проблемы.
Первая проблема — фрагментация данных. Представьте, что информация о банковском счете клиента разбросана по 50 различным файлам: первоначальное создание счета в одном файле, первое пополнение в другом, снятие средств в третьем, и так далее. Чтобы получить текущий баланс, системе приходится просматривать все эти файлы, выбирая самую свежую версию данных. Это явление называется амплификацией чтения — когда для получения одной логической записи требуется множество физических операций чтения.
Вторая проблема — избыточность данных. В ScyllaDB обновление данных не перезаписывает старое значение, а создает новую версию. Если клиент изменил свой адрес 10 раз, в системе будут храниться все 10 версий адреса, хотя актуальна только последняя. Более того, удаление данных тоже не освобождает место немедленно — вместо этого создается специальная метка, называемая tombstone, которая говорит: "эти данные считаются удаленными с такого-то момента времени".
Третья проблема — деградация производительности. С ростом количества файлов увеличивается не только время поиска нужных данных, но и объем метаданных, которые система должна держать в памяти: информация о диапазонах ключей в каждом файле, статистика для оптимизатора запросов, Bloom-фильтры для быстрой проверки наличия ключа.
Компактизация как решение: искусство управления хаосом
Компактизация — это процесс, который берет множество маленьких SSTable и объединяет их в меньшее количество больших файлов, попутно решая все три описанные проблемы. Во время компактизации происходит несколько важных вещей.
Во-первых, данные об одном и том же ключе, разбросанные по разным файлам, объединяются. Из всех версий остается только самая актуальная. Это существенно улучшает производительность чтения — теперь для получения записи нужно обратиться к одному файлу вместо десятков.
Во-вторых, устаревшие данные и tombstone-маркеры, срок жизни которых истек, физически удаляются. Это освобождает дисковое пространство и уменьшает объем данных, которые система должна просматривать при каждом запросе.
В-третьих, данные переупорядочиваются оптимальным образом, улучшая локальность доступа и эффективность кеширования на уровне операционной системы.
Но здесь начинается самое интересное: существует множество способов выполнять компактизацию, и каждый имеет свои преимущества и недостатки. Выбор стратегии компактизации — это выбор между скоростью записи и скоростью чтения, между эффективностью использования дискового пространства и нагрузкой на процессор, между предсказуемостью производительности и средней пропускной способностью.
Size-Tiered Compaction Strategy (STCS): простота и эффективность записи
Size-Tiered Compaction Strategy была первой стратегией, реализованной в Apache Cassandra, и остается стратегией по умолчанию как в Cassandra, так и в ScyllaDB. Ее популярность объясняется простотой концепции и отличной производительностью для определенных типов нагрузок.
Принцип STCS: объединяем подобное с подобным
Основная идея STCS проста: файлы схожего размера объединяются вместе. Это похоже на то, как вы могли бы сгруппировать разные монеты: сначала собираете кучки по 10 монет одного номинала, затем меняете 10 кучек на одну купюру большего номинала, и так далее.
В контексте ScyllaDB это работает следующим образом. Когда накапливается достаточное количество (по умолчанию 4) SSTable примерно одинакового размера, они объединяются в одну большую SSTable. Большие SSTable, в свою очередь, тоже накапливаются и объединяются в еще большие, создавая иерархию SSTable по размерам.
Однако реальность сложнее простой аналогии с монетами. SSTable редко бывают точно одинакового размера, поэтому STCS использует концепцию "buckets" (корзин). Каждая корзина охватывает определенный диапазон размеров — например, от 50 до 150 МБ. Когда в корзине накапливается достаточно файлов, они компактизируются вместе.
Детали алгоритма группировки (bucketing) и пример кода настройки STCS:
def calculate_bucket_bounds(sstable_size, bucket_low=0.5, bucket_high=1.5):
"""
Вычисляет границы корзины для SSTable заданного размера.
SSTable попадает в bucket, если его размер находится между:
average_size * bucket_low и average_size * bucket_high
"""
# Пример: для среднего размера 100MB и стандартных параметров
# bucket будет включать файлы от 50MB до 150MB
avg_size = get_bucket_average_size(sstable_size)
return (avg_size * bucket_low, avg_size * bucket_high)
-- Настройка STCS для таблицы с интенсивной записью
CREATE TABLE user_activity (
user_id UUID,
activity_time timestamp,
activity_type text,
details text,
PRIMARY KEY (user_id, activity_time)
) WITH compaction = {
'class': 'SizeTieredCompactionStrategy',
'min_threshold': '4', -- Минимум файлов для начала компактизации
'max_threshold': '32', -- Максимум файлов в одной компактизации
'bucket_high': '1.5', -- Верхняя граница корзины по размеру
'bucket_low': '0.5', -- Нижняя граница корзины по размеру
'min_sstable_size': '50' -- Минимальный размер SSTable в MB
};
Математика STCS
Чтобы понять сильные и слабые стороны STCS, давайте рассмотрим математику, стоящую за этой стратегией. Предположим, у нас есть база данных размером 1 ТБ, и мы записываем данные со скоростью 100 МБ в секунду.
В идеальном случае, когда данные только добавляются и никогда не обновляются, STCS демонстрирует логарифмическую амплификацию записи. Это означает, что каждый байт данных будет переписан в среднем log₄(размер_базы / размер_memtable) раз. Для нашего примера это примерно 4-5 раз, что является отличным показателем.
Однако у STCS есть ахиллесова пята — требования к дисковому пространству. Представьте худший сценарий: у вас есть два SSTable по 500 ГБ каждый, и система решает их скомпактировать. Для создания нового файла размером 1 ТБ потребуется 1 ТБ свободного места, при том что исходные файлы будут удалены только после завершения компактизации. Это означает, что в худшем случае STCS требует 100% дополнительного дискового пространства, то есть эффективно вы можете использовать всего лишь половину вашего диска для хранения данных!
Когда STCS является хорошей стратегией
STCS превосходно работает в нескольких сценариях. Первый — это write-heavy нагрузка, где операции записи значительно преобладают над чтением. Системы сбора логов, метрик, IoT-данных — все они генерируют огромные потоки данных, которые записываются один раз и редко читаются.
Второй сценарий — append-only данные, где записи только добавляются, но не обновляются. Исторические данные, логи событий, архивы — все это примеры таких данных. В этом случае STCS демонстрирует минимальную амплификацию записи и эффективно использует дисковое пространство.
Третий сценарий — временные данные с коротким сроком жизни. Кеши сессий, временные вычисления, буферы сообщений — если данные все равно будут удалены через несколько часов или дней, нет смысла тратить ресурсы на их идеальную организацию.
Подводные камни STCS: чего следует остерегаться
Однако STCS может стать кошмаром в неподходящих сценариях. Главная проблема возникает с часто обновляемыми данными. Представьте таблицу пользовательских профилей, где каждый вход в систему обновляет поле last_login. Со временем информация о каждом пользователе будет разбросана по десяткам SSTable, что катастрофически скажется на производительности чтения.
Вторая проблема — tombstones. В STCS удаленные данные могут очень долго занимать место на диске. Если у вас есть большой SSTable с несколькими tombstones, эти метки удаления будут жить до тех пор, пока этот большой файл не будет скомпактирован с другими большими файлами такого же размера, что может произойти очень нескоро.
Третья проблема — "температурные пики" при компактизации больших файлов. Когда система решает объединить несколько файлов по 100 ГБ каждый, это создает огромную нагрузку на дисковую подсистему, что может привести к временному увеличению задержки для пользовательских запросов.
Краткие выводы по стратегии компакции STCS:
Преимущества STCS:
Минимальная амплификация записи (данные перезаписываются редко)
Простота понимания и настройки
Отличная производительность для write-heavy нагрузок
Низкое потребление CPU
Недостатки STCS:
Проблема пространства: для объединения двух файлов по 50 ГБ нужно 100 ГБ свободного места
Деградация чтения: со временем накапливается много больших файлов
"Температурные пики": периодические всплески I/O при компактизации гигантских файлов и рост задержки пользовательских запросов
Leveled Compaction Strategy (LCS): когда каждая миллисекунда на счету
Leveled Compaction Strategy представляет собой совершенно иной подход к организации данных. Если стратегия STCS — это "прагматик", готовый мириться с некоторым беспорядком ради эффективности, то LCS — это "перфекционист", стремящийся к идеальной организации данных любой ценой.
Архитектура LCS: иерархическая организация данных
LCS организует данные в строгую иерархию уровней, где каждый уровень имеет четко определенный размер и структуру. Это похоже на идеально организованную картотеку в библиотеке, где каждый уровень представляет все более детальную категоризацию.
Уровень 0 (L0) — это корзина, куда попадают новые SSTable из Memtable. Здесь царит относительный хаос — файлы могут перекрываться по диапазонам ключей, но это лишь временное состояние. Аналогия с библиотекой: на этом уровне у нас лежат неотсортированные новые карточки.
Уровень 1 (L1) — это первый уровень организации данных. Он содержит ровно 10 SSTable (при настройках по умолчанию), каждый размером 160 МБ. Критически важно, что эти файлы не перекрываются — каждый ключ находится ровно в одном файле на уровне. Аналогия с библиотекой: тут у нас 10 ящиков, каждый с карточками на определенную букву
Уровень 2 (L2) содержит 100 файлов, уровень 3 (L3) — 1000, и так далее. Каждый следующий уровень в 10 раз больше предыдущего. Эта экспоненциальная структура означает, что подавляющее большинство данных (около 90%) находится на самом высоком уровне. Аналогия с библиотекой: теперь у нас 100 ящиков с более детальной сортировкой.
Процесс компактизации в LCS — это постоянное движение данных вверх по иерархии. Когда L0 переполняется, все его файлы компактизируются с L1. Когда L1 становится слишком большим, один файл из L1 выбирается и компактизируется с соответствующими файлами из L2.
Пример как работает иерархия уровней:
L0: Новые данные из memtable, файлы могут перекрываться
L1: 10 × 160MB = 1.6GB данных, без перекрытий
L2: 100 × 160MB = 16GB данных, без перекрытий
L3: 1000 × 160MB = 160GB данных, и так далее
Математика амплификации записи в LCS:
def calculate_write_amplification_lcs(data_size_gb, sstable_size_mb=160, fanout=10):
"""
Вычисляет теоретическую амплификацию записи для LCS
"""
levels = math.ceil(math.log(data_size_gb * 1024 / sstable_size_mb) / math.log(fanout))
# Данные переписываются на каждом уровне
write_amplification = levels + 1 # +1 для начальной записи
# На практике хуже из-за перекрытий
practical_amplification = write_amplification * 1.5
return practical_amplification
Пример кода с LCS стратегией:
-- Настройка LCS для финансовых транзакций
CREATE TABLE account_transactions (
account_id UUID,
transaction_id UUID,
amount decimal,
timestamp timestamp,
status text,
PRIMARY KEY (account_id, timestamp, transaction_id)
) WITH compaction = {
'class': 'LeveledCompactionStrategy',
'sstable_size_in_mb': '160', -- Размер каждого SSTable файла
'fanout_size': '10' -- Множитель размера между уровнями
} AND gc_grace_seconds = 864000; -- 10 дней для синхронизации tombstones
Превосходство LCS в операциях чтения
Превосходство стратегии LCS проявляется при выполнении операций чтения. Благодаря отсутствию перекрытий на каждом уровне, для поиска ключа нужно проверить максимум один файл на каждом уровне. В типичной базе данных с 5-6 уровнями это означает проверку всего 5-6 файлов вместо потенциально сотен в STCS.
Около 90% данных находится на последнем уровне, поэтому статистически в 90% случаев нужные данные будут найдены с первой попытки. Это обеспечивает не только низкую среднюю задержку, но и высокую предсказуемость — P99 задержка близка к средней, что критично для систем реального времени.
Цена - амплификация записи
За преимущество в чтении приходится платить высокую цену. В LCS данные постоянно перемещаются между уровнями. Рассмотрим путь одной записи: сначала она записывается в L0, затем компактизируется в L1, потом в L2, L3, и так далее. На каждом уровне данные физически переписываются.
В худшем случае, для базы данных с 6 уровнями, каждый байт может быть переписан более 50 раз! Это создает огромную нагрузку на дисковую подсистему и может значительно сократить срок службы SSD.
Когда LCS незаменима
LCS становится оптимальным выбором в нескольких критических сценариях. Первый — это системы с жесткими требованиями к задержке операций чтения. Финансовые транзакции, системы реального времени, критически важные API — везде, где задержка в несколько миллисекунд может стоить денег или репутации.
Второй сценарий — данные с высокой частотой чтения и относительно низкой частотой записи. Каталоги продуктов, справочники, конфигурационные данные — все это примеры данных, которые читаются тысячи раз на каждую запись.
Третий сценарий — небольшие наборы данных с критическими требованиями к производительности. Если ваша база данных измеряется гигабайтами, а не терабайтами, накладные расходы от амплификации записи могут быть приемлемыми ради превосходной производительности чтения.
Ловушка LCS: когда стремление к совершенству становится проблемой
Главная опасность LCS — это то, что я назвал бы "компактизационным штормом" (или проще - лавинообразным ростом компакций). При интенсивной записи данных может возникнуть ситуация, когда система не успевает перемещать данные между уровнями. L0 переполняется, что блокирует новые записи. Это может привести к каскадному эффекту, когда вся система временно останавливается.
Вторая проблема — износ SSD. Постоянная перезапись данных может значительно сократить срок службы дисков. Один из наших клиентов обнаружил, что при использовании LCS их SSD выходили из строя в 3 раза быстрее, чем ожидалось.
Третья проблема — сложность настройки. Размер SSTable (по умолчанию 160 МБ) критически важен. Слишком маленький размер приведет к огромному количеству файлов и накладным расходам на метаданные (как в Hadoop с его проблемой мелких файлов). Слишком большой размер — к долгим компактизациям и всплескам латентности.
Суммируя,
Преимущества LCS:
Превосходная производительность чтения (часто нужно проверить всего 1-2 файла)
Минимальная амплификация пространства (10-20% overhead)
Эффективное удаление tombstones
Предсказуемая задержка
Недостатки LCS:
Экстремальная амплификация записи: данные могут быть перезаписаны 10-50 раз с сопутствующим износом SSD
Высокая нагрузка на CPU и I/O
"Компактизационный шторм": при интенсивной записи может возникнуть каскад компактизаций
Time Window Compaction Strategy (TWCS): стратегия для временных рядов
Time Window Compaction Strategy — это специализированное решение для временных рядов. Если STCS и LCS пытаются быть универсальными стратегиями, то TWCS — это узкоспециализированный эксперт, непревзойденный в своей области.
Основная идея TWCS проста: данные группируются не по размеру или уровню, а по времени создания. Представьте архив документов, где папки организованы по месяцам. Когда приходит время уничтожить старые документы согласно политике хранения, вы просто берете папки старше определенной даты и отправляете их в шредер, без перебора каждого документа. Никакой сортировки, никакого просмотра отдельных документов не треубется.
TWCS делит время на фиксированные окна — часы, дни или недели. Все данные, записанные в течение одного временного окна, группируются вместе. Внутри активного окна работает обычный STCS, но ключевое отличие в том, что происходит, когда окно закрывается.
Когда временное окно заканчивается, все SSTable в этом окне компактизируются в один или несколько больших файлов, и — что критически важно запомнить — эти файлы больше никогда не будут компактизироваться с файлами из других временных окон. Это означает, что старые данные остаются нетронутыми, что идеально для временных рядов с TTL (Time To Live).
TTL в TWCS
Когда TTL данных в SSTable истекает, весь файл может быть удален одной атомарной операцией. Никакой компактизации, никакого просмотра отдельных записей — просто удаление файла с диска.
Рассмотрим конкретный пример. У вас есть система мониторинга, которая хранит метрики за последние 30 дней. С TWCS и дневными окнами, каждый день в полночь система автоматически удаляет файлы, содержащие данные 31-дневной давности. Это происходит мгновенно и без какой-либо нагрузки на CPU или I/O.
Вкратце весь процесс выглядит так:
Новые данные попадают в "активное" окно (например, текущие 6 часов)
Внутри окна работает обычный STCS
Когда окно "закрывается", все его файлы объединяются в один
Закрытые окна больше никогда не компактизируются
При истечении TTL целое окно удаляется одной операцией
Золотые правила настройки TWCS
Выбор правильного размера временного окна — это искусство, основанное на глубоком понимании ваших данных и паттернов доступа. Основное правило: стремитесь к 20-30 окнам на весь период хранения данных. Слишком маленькие окна приведут к большому количеству файлов и накладным расходам на метаданные. Слишком большие — к неэффективной компактизации и задержкам в удалении устаревших данных.
Для данных с коротким TTL (часы или дни) используйте маленькие окна. Например, для кеша сессий с 24-часовым TTL идеальными будут часовые окна. Для долгоживущих данных (месяцы или годы) подойдут дневные или даже недельные окна (примеры: для 90 дней хранения используйте 3-4 дневные окна, а для 7 дней хранения — 4-6 часовые окна).
Пример кода вычисления оптимального размера окна для TWCS:
def calculate_optimal_window_size(ttl_days, target_windows=20):
"""
Вычисляет оптимальный размер окна для TWCS
Цель: 20-30 окон на время жизни данных
"""
window_size_hours = (ttl_days * 24) / target_windows
# Округляем до удобных значений
if window_size_hours <= 1:
return 1, 'HOURS'
elif window_size_hours <= 6:
return int(window_size_hours), 'HOURS'
elif window_size_hours <= 24:
return int(window_size_hours / 6) * 6, 'HOURS'
else:
return int(window_size_hours / 24), 'DAYS'
Критически важно понимать природу ваших временных данных. TWCS предполагает, что данные записываются примерно в хронологическом порядке. Если вы записываете данные с timestamp из прошлого (например, загружаете исторические данные), эффективность TWCS резко падает.
Подводные камни TWCS
Главная опасность TWCS — неправильное использование для данных без временной компоненты. Мы видели случаи, когда разработчики пытались использовать TWCS для обычных таблиц, добавляя искусственный timestamp. Это приводило к катастрофической деградации производительности.
Вторая проблема — out-of-order записи. Если вы записываете данные с timestamp из прошлого, они могут попасть в уже закрытые окна, нарушая эффективность стратегии.
Третья проблема — запросы, пересекающие множество окон. Если вам часто нужны данные за длительный период (например, отчет за год), TWCS может быть неэффективна, так как придется читать файлы из множества временных окон.
Расширенные возможности TWCS в ScyllaDB 5.x +
Последние версии ScyllaDB значительно улучшили TWCS. Одно из ключевых нововведений — cross-window tombstone compaction: теперь tombstones очищаются даже между разными buckets, решая проблему накопления удаленных данных. В классическом TWCS tombstones могли застревать в старых окнах, занимая место даже после истечения gc_grace_seconds. Теперь система может выборочно компактизировать файлы из разных окон специально для удаления tombstones.
Другое важное улучшение — unsafe aggressive SSTable expiration. Эта опция позволяет системе более агрессивно определять и удалять полностью истекшие SSTable, даже если есть небольшой риск воскрешения удаленных данных. Для многих use cases, особенно с append-only данными, этот риск приемлем ради значительной экономии дискового пространства.
Пример кода с TWCS стратегией:
-- TWCS для системы мониторинга с 30-дневным хранением
CREATE TABLE system_metrics (
server_id UUID,
metric_time timestamp,
cpu_usage float,
memory_usage float,
disk_io_rate float,
PRIMARY KEY (server_id, metric_time)
) WITH compaction = {
'class': 'TimeWindowCompactionStrategy',
'compaction_window_unit': 'HOURS',
'compaction_window_size': '6', -- 6-часовые окна
'timestamp_resolution': 'MICROSECONDS',
'tombstone_threshold': '0.2', -- Компактизация при 20% tombstones
'tombstone_compaction_interval': '86400', -- Проверка tombstones раз в день
'unchecked_tombstone_compaction': 'true' -- Компактизация между окнами
} AND default_time_to_live = 2592000; -- 30 дней TTL
Итого,
Преимущества TWCS:
Стратегия идеально подходит для данных с TTL
Минимальный рост используемого места на диске
Эффективное удаление старых данных
Отличная производительность для запросов по времени
Недостатки TWCS:
Не подходит для данных без временной компоненты
Проблемы при запросах с многочисленными временными окнами в запросе
Требует тщательной настройки размера окна
Когда TWCS — единственно правильный выбор
TWCS незаменима для временных рядов с четко определенным жизненным циклом. Логи приложений, метрики мониторинга, данные IoT-сенсоров, исторические котировки — все эти данные имеют временную природу и ограниченный срок хранения.
Ключевые индикаторы того, что вам нужна TWCS: данные имеют временную метку как часть первичного ключа, есть четкая политика retention (7 дней, 30 дней, 1 год), большинство запросов касается недавних данных, старые данные читаются редко или никогда.
Пример кейса из телекома: оператор связи хранит 10 миллиардов CDR (Call Detail Records) с 90-дневным retention (или же значительно больше по закону Яровой). TWCS с 3-дневными окнами обеспечивает стабильное использование диска и автоматическое удаление устаревших данных без ручного вмешательства.
Incremental Compaction Strategy (ICS): если у компании есть деньги на ScyllaDB Enterprise
Incremental Compaction Strategy — это уникальная фича ScyllaDB, недоступная в Apache Cassandra. Это не просто одна из стратегий компактизации — это переосмысление того, как должна работать компактизация в современных распределенных системах.
Проблема, которую решает ICS
Чтобы понять ICS, вернемся к главной проблеме STCS — требованию 100% дополнительного дискового пространства. В эпоху облачных вычислений, где хранилище стоит денег, держать половину диска пустой — это расточительство.
Традиционный подход к компактизации можно сравнить с переездом в новый дом: вы не можете освободить старый дом, пока полностью не переехали в новый. ICS меняет эту парадигму, позволяя "переезжать по частям". Вместо создания одного гигантского файла, ICS создает "run" — серию небольших фрагментов (shards) с неперекрывающимися диапазонами данных. Это как разбить большую энциклопедию на отдельные главы, которые можно обновлять независимо. В традиционном подходе (STCS) вся энциклопедия — это один огромный том. Чтобы обновить ее, нужно перепечатать весь том. В подходе ICS энциклопедия разделена на тома по буквам. Чтобы обновить информацию о словах на букву "А", нужно перепечатать только соответствующий том.Вместо создания одного огромного выходного файла, ICS создает серию фрагментов. Как только фрагмент готов, он немедленно заменяет соответствующие входные данные, освобождая место. Это означает, что дисковое пространство освобождается постепенно, по мере прогресса компактизации. Каждый фрагмент — это полноценный SSTable размером 1 ГБ (можно задать и другой размер), содержащий определенный диапазон ключей.
И теперь совсем вкратце как работает ICS:
SSTable разбивается на фрагменты по 1GB
Каждый фрагмент покрывает определенный диапазон токенов
Компактизация происходит пофрагментно
Не нужно временное пространство для всего файла
Пример кода:
-- ICS для высоконагруженной e-commerce платформы
CREATE TABLE shopping_carts (
user_id UUID,
cart_id UUID,
last_modified timestamp,
items list<frozen<item>>,
total_amount decimal,
PRIMARY KEY (user_id, cart_id)
) WITH compaction = {
'class': 'IncrementalCompactionStrategy',
'sstable_size_in_mb': '1000', -- 1GB фрагменты
'space_amplification_goal': '1.5' -- Целевая амплификация пространства
};
Эффективность ICS: математика на пальцах
Для традиционного STCS самый худший случай требует 100% дополнительного пространства.
Для ICS формула выглядит так: Дополнительное пространство = (Количество шардов × log₄(размер диска на шард)) × 2 ГБ
Для типичного сервера с 14 CPU (14 шардов) и 1 ТБ диска это составляет всего около 86 ГБ, или 8.4% — огромная разница по сравнению с 100%! По итогу мы покупаем ICS, но экономим уйму денег на закупке дисков!
Самый простой пример для большей понятности:
STCS: 1TB данных → требуется 2TB места → компактизация занимает часы
ICS: 1TB данных → требуется около 1.1TB места → компактизация идет постепенно
Ограничения ICS
Главное ограничение ICS — доступность только в ScyllaDB Enterprise. Это коммерческий продукт, и для многих проектов покупка лицензии может быть непреодолимым препятствием.
Второе ограничение — сложность. Хотя ICS скрывает свою сложность от пользователя, внутри она значительно сложнее STCS. Это может затруднить отладку в редких краевых случаях в продакшене.
Итого,
Преимущества ICS:
90% утилизация диска вместо 50% у STCS
Отсутствие "температурных пиков"
Инкрементальный прогресс компактизации
Совместимость с STCS (легкая миграция)
Недостатки ICS:
Немного выше сложность реализации
Больше метаданных для отслеживания фрагментов
Доступна только в ScyllaDB (не в Cassandra)
Когда ICS — оптимальный выбор
ICS становится практически универсальным выбором для пользователей ScyllaDB Enterprise. Она сочетает простоту STCS с эффективностью использования пространства, близкой к LCS, без экстремальной амплификации записи последней.
ICS идеальна для больших наборов данных, где стоимость хранилища значительна. Если ваши таблицы измеряются терабайтами, экономия 40% дискового пространства может означать десятки тысяч долларов в год.
ICS также отлично подходит для смешанных нагрузок с непредсказуемыми паттернами. В отличие от других стратегий, которые оптимизированы для конкретных сценариев, ICS обеспечивает хорошую производительность.
Пример из продакшена: стриминговый сервис типа Netflix или ему подобного, где данных петабайты, и закупка 50% дополнительного дискового пространства для STCS будет стоить огромных сумм денег. Вместо использования STCS лучше купить enterprise-версию ScyllaDB и использовать имеющиеся диски на 90% вместо 50% при STCS с дополнительной закупкой новых дисков. Задержка также будет ниже в силу более плавной и предсказуемой компактизации.
Компактизация в контексте распределенной системы
До сих пор мы рассматривали компактизацию как локальный процесс на одном узле. Но ScyllaDB — это распределенная система, и компактизация имеет глубокие последствия для консистентности, репликации и общей надежности системы.
Tombstones и распределенное удаление
В распределенной системе удаление данных — это сложная проблема. Когда вы удаляете запись на одном узле, как другие узлы узнают об этом? ScyllaDB решает эту проблему с помощью tombstones — специальных маркеров удаления.
Но tombstones создают новую проблему: они должны жить достаточно долго, чтобы распространиться на все реплики. Это время контролируется параметром gc_grace_seconds (по умолчанию 10 дней). Только после истечения этого периода tombstones могут быть безопасно удалены во время компактизации.
Рассмотрим пример: компания использовала агрессивные настройки с gc_grace_seconds = 3600 (1 час) для экономии места. Все работало отлично, пока один из узлов не вышел из строя на 2 часа. Когда узел вернулся в строй, произошло "воскрешение" удаленных данных — узел не знал, что эти данные были удалены, так как tombstones уже были очищены на других узлах.
Поэтому tombstones должны жить достаточно долго, чтобы реплицироваться на все узлы:
-- Настройка gc_grace_seconds для разных сценариев
-- Стандартное значение: 10 дней
ALTER TABLE distributed_data WITH gc_grace_seconds = 864000;
-- Для часто синхронизируемых данных можно уменьшить
ALTER TABLE frequently_synced WITH gc_grace_seconds = 86400; -- 1 день
-- Для критичных данных с редкими repair
ALTER TABLE critical_data WITH gc_grace_seconds = 2592000; -- 30 дней
Repair и компактизация: сложные взаимоотношения
Repair — это процесс синхронизации данных между репликами. Он критически важен для поддержания консистентности, но его взаимодействие с компактизацией может создавать неожиданные эффекты.
Во время repair данные могут копироваться между узлами, создавая новые SSTable. Это может нарушить тщательно выстроенную структуру уровней в LCS или временных окон в TWCS.
Особенно проблематична ситуация с TWCS. Представьте, что repair копирует данные месячной давности. Эти "старые" данные попадут в текущее временное окно, нарушая основной принцип TWCS — группировку по времени создания. Некоторые компании решают эту проблему, запуская repair только для последних временных окон, принимая риск некоторой неконсистентности в старых данных.
Пример repair в зависимости от стратегии компактизации:
def optimize_repair_with_compaction(table_name):
"""
Оптимизация repair с учетом стратегии компактизации
"""
strategy = get_compaction_strategy(table_name)
if strategy == "TWCS":
# Для TWCS repair только последних окон
return f"nodetool repair -pr -st {get_recent_windows_tokens()}"
elif strategy == "LCS":
# Для LCS можно использовать subrange repair
return "nodetool repair -pr --in-local-dc -sub"
else:
# Стандартный repair для STCS/ICS
return "nodetool repair -pr --in-local-dc"
Влияние компактизации на производительность кластера
Компактизация потребляет значительные ресурсы: CPU для сортировки и слияния данных, I/O для чтения и записи файлов, память для буферов. В распределенной системе это создает интересные эффекты.
Представьте кластер из 100 узлов. Если все узлы начнут тяжелую компактизацию одновременно, это может привести к деградации производительности всего кластера с лавинообразным ростом задержки запросов. ScyllaDB решает эту проблему несколькими способами:
Во-первых, компактизация работает с приоритетом ниже чем у пользовательских запросов. Если поступает запрос от клиента, компактизация приостанавливается, чтобы освободить ресурсы.
Во-вторых, скорость компактизации ограничена параметром compaction_throughput_mb_per_sec. Это предотвращает ситуацию, когда компактизация "съедает" всю пропускную способность диска.
В-третьих, в ScyllaDB 5.x появился автономный контроллер компактизации, который динамически регулирует ресурсы на основе текущей нагрузки.
Автономный контроллер компактизации
ScyllaDB 5.x получила функцию автономного контроллера компактизации, использующего принципы теории автоматического управления.
Проблема, которую решает контроллер
В традиционном подходе администратор статически настраивает параметры компактизации: сколько CPU и I/O может использовать процесс. Но оптимальные значения постоянно меняются в зависимости от нагрузки.
Утром, когда пользователи проверяют почту и новости, нагрузка на чтение высокая — компактизация должна работать минимально. Ночью, когда активность низкая, компактизация может работать на полную мощность. В течение дня происходят всплески и спады. Статическая конфигурация не может адаптироваться к этим изменениям.
Как работает контроллер: теория управления в действии
Контроллер использует PID-алгоритм (Proportional-Integral-Derivative) из теории автоматического управления. Он постоянно измеряет "backlog" — объем работы, которую нужно выполнить для приведения данных в оптимальное состояние.
Для каждой стратегии компактизации backlog вычисляется по-разному:
Для STCS: размер данных, которые можно скомпактировать
Для LCS: объем данных не на финальном уровне
Для TWCS: размер активных временных окон
Для ICS: фрагменты, требующие компактизации
Контроллер стремится поддерживать backlog на минимальном уровне, но без ущерба для пользовательских запросов. Он динамически регулирует CPU и I/O shares, выделенные для компактизации, реагируя на изменения нагрузки в реальном времени.
Когда использовать автономный контроллер
Очень просто - когда видим рост задержки запросов во время тяжелых компактизаций. Администраторы БД раньше постоянно корректировали параметры вручную, пытаясь найти баланс, но ныне, используя контроллер, можно сделать задержку значительно более стабильной, и при этом общая производительность системы вырастет, так как контроллер более эффективно утилизирует доступные ресурсы в периоды низкой нагрузки.
Немного техники: архитектура контроллера и метрики контроллера для мониторинга
# Конфигурация в scylla.yaml
compaction_controller:
enabled: true
# Целевой backlog (0 = держать минимальным)
target_backlog: 0
# Период корректировки shares
shares_adjustment_period: 30s
# Минимальные shares для компактизации
min_compaction_shares: 50
# Максимальные shares для компактизации
max_compaction_shares: 1000
# PromQL для Grafana
# Backlog по таблицам
scylla_compaction_backlog_bytes{keyspace="production",table="events"}
# Текущие shares компактизации
scylla_scheduler_shares{group="compaction",shard="0"}
# Эффективность контроллера
rate(scylla_compaction_controller_adjustments_total[5m])
Выбор стратегии: алгоритм действий
После глубокого погружения в каждую стратегию, давайте рассмотрим систематический подход к выбору оптимальной стратегии для вашего use case.
Анализ нагрузки
Первый шаг — анализ вашей нагрузки. Ответьте на следующие вопросы:
Каково соотношение операций чтения и записи? Если записей значительно больше (70%+), рассматривайте STCS или ICS. Если преобладает чтение — LCS может быть оптимальным выбором.
Как часто обновляются существующие данные? Частые обновления плохо работают с STCS, но хорошо с LCS. ICS обеспечивает разумный баланс.
Есть ли временная компонента в данных? Если да, и особенно если есть TTL, TWCS должна быть главным кандидатом.
Каковы требования к задержке? Если нужна стабильная низкая задержка при чтении, LCS трудно превзойти. Если допустима некоторая вариативность — другие стратегии могут быть эффективнее.
Каков бюджет на хранилище? Если дисковое пространство дорого или ограничено, избегайте STCS в пользу ICS или LCS.
Итак, допустим вы ответили на предыдущие вопросы, но все еще сомневаетесь в выборе стратегии. Ниже я привожу дополнительные критерии выбора в более конкретном формате:
-
Соотношение чтения/записи
Write-heavy (>70% записей) → STCS или ICS
Read-heavy (>70% чтений) → LCS
Сбалансированная нагрузка на чтение и запись → ICS
-
Паттерны доступа к данным
Случайный доступ → LCS
Последовательный по времени → TWCS
Смешанный → ICS или STCS
-
Характер данных
Временные ряды с TTL → TWCS
Часто обновляемые записи → LCS
Append-only → STCS или ICS
-
Ресурсные ограничения
Ограниченное дисковое пространство → ICS или LCS
Ограниченный CPU → STCS
Нужна предсказуемая latency → LCS
Вот совсем упрощенная матрица для первичного выбора:
Если данные имеют TTL и временную природу → TWCS
Иначе, если чтение критично и преобладает → LCS
Иначе, если доступна ScyllaDB Enterprise → ICS
Иначе → STCS с планом миграции
Отраслевые паттерны
Выбор стратегии компактизации также зависит не только от ответов на предыдущие вопросы, но и от отрасли, в которой работает компания.
Финансовые услуги: лучше выбрать LCS для критически важных данных (балансы, транзакции) и TWCS для исторических данных и логов. Предсказуемость и низкая задержка важнее эффективности использования ресурсов.
E-commerce: лучше выбрать ICS как универсальное решение, с LCS для каталогов продуктов (где критична скорость поиска) и TWCS для пользовательской активности и логов.
Телекоммуникации с их огромными объемами временных данных: лучше выбрать TWCS для CDR и сетевых метрик, используя ICS для справочных данных.
Стриминговые сервисы и CDN: выбирайте ICS за эффективное использование пространства при огромных объемах данных, используя TWCS для аналитики просмотров.
Миграция между стратегиями
Одно из преимуществ ScyllaDB — возможность изменить стратегию компактизации без остановки сервиса. Но это не означает, что миграция проста в реализации. Напротив, она требует планирования, мониторинга и готовности к неожиданностям.
Общие принципы безопасной миграции
Независимо от направления миграции, следуйте этим принципам:
Никогда не мигрируйте вслепую. Сначала протестируйте на staging-окружении с репрезентативными данными и нагрузкой. Измерьте влияние на производительность, использование ресурсов и дискового пространства.
Мигрируйте постепенно. Начните с наименее критичных таблиц. Получите опыт, выявите подводные камни, отработайте процедуры.
Мониторьте постоянно. Во время миграции следите за всеми метриками: задержка, throughput, CPU, I/O, дисковое пространство, количество SSTable, pending compactions. Для этого есть прекрасные дэшборды для Grafana от ScyllaDB.
Имейте план отката. Всегда знайте, как вернуться к предыдущей стратегии. Сохраняйте конфигурацию, делайте снэпшоты если возможно.
Планируйте ресурсы. Миграция может временно увеличить использование CPU и I/O. Убедитесь, что у системы есть запас прочности.
Особенности миграции для каждого направления
STCS → ICS обычно самая безопасная миграция. ICS разработана как улучшенная версия STCS, поэтому переход относительно гладкий. Основной выигрыш — освобождение дискового пространства — происходит постепенно по мере естественной компактизации.
STCS → LCS более рискованна. Внезапный переход от больших файлов к маленьким создает всплеск активности компактизации. CPU и I/O нагрузка может вырасти в разы. Рекомендуется сначала снизить compaction_throughput перед миграцией и постепенно увеличивать его.
LCS → STCS/ICS гораздо более сложна. Пример: у вас есть сотни маленьких 160MB файлов, которые нужно объединить в большие. Это может занять дни или недели и создать временные проблемы с производительностью чтения.
Любая стратегия → TWCS требует особой осторожности. TWCS ожидает, что данные организованы по времени. Существующие данные окажутся в "неправильных" окнах. Обычно лучше создать новую таблицу с TWCS и мигрировать данные, чем менять стратегию на существующей таблице.
Мониторинг и troubleshooting: держим руку на пульсе
Эффективная эксплуатация ScyllaDB невозможна без глубокого мониторинга процессов компактизации. Рассмотрим ключевые метрики и типичные проблемы.
Ключевые метрики: что действительно важно
Pending compactions — количество задач компактизации в очереди. Это главный индикатор того, справляется ли система с нагрузкой. Нормальные значения зависят от стратегии: для STCS/ICS — до 50, для LCS — до 100-200, для TWCS — до 30.
SSTable count per table — общее количество файлов. Рост этой метрики указывает на то, что компактизация не успевает за записью. Для STCS ожидайте логарифмический рост вместе с размером данных. Для LCS количество должно быть относительно стабильным.
Average SSTable per read — сколько файлов в среднем читается для одного запроса. Для LCS должно быть близко к 1, для STCS/ICS — менее 5, для TWCS зависит от диапазона запроса.
Compaction throughput — скорость компактизации в MB/s. Сравните с скоростью записи — compaction throughput должен быть в 10-30 раз выше write throughput для поддержания здорового состояния.
Disk usage percentage — критически важно для STCS. При превышении 50% риск исчерпания места при компактизации резко возрастает.
Типичные проблемы и их решения
"Compaction can't keep up" — самая частая проблема. Pending compactions постоянно растут, SSTable накапливаются, производительность деградирует.
Решение зависит от причины. Если показатель write rate превышает возможности компактизации, увеличьте compaction_throughput_mb_per_sec. Если CPU перегружен, добавьте больше ядер или узлов. Если проблема в стратегии — рассмотрите миграцию.
"Out of disk space during compaction" — критическая ситуация, особенно с STCS. Система не может продолжить компактизацию из-за нехватки места.
Немедленные действия: остановите компактизацию (команда nodetool stop compaction), удалите снэпшоты (nodetool clearsnapshot), запустите очистку для удаления данных, не принадлежащих узлу (nodetool cleanup). Долгосрочное решение — миграция на ICS или добавление дискового пространства.
"High read latency spikes" — периодические всплески задержки чтения, часто коррелирующие с компактизацией больших файлов.
Для STCS/ICS проверьте, не происходит ли компактизация очень больших файлов в моменты всплесков. Для LCS это может указывать на "компактизационный шторм". Решение — тюнинг параметров стратегии или миграция.
Инструменты мониторинга
ScyllaDB предоставляет богатый набор метрик через JMX и Prometheus. Большинство команд используют стек Prometheus + Grafana для мониторинга, с готовыми дашбордами от ScyllaDB.
Ключевые дашборды для мониторинга компактизации:
Overview: общее состояние кластера
CQL Performance: задержка операций
OS Level Metrics: CPU, I/O, память
Compaction: специализированный дашборд для анализа компактизации
Будущее компактизации в ScyllaDB: что нас ждет
ScyllaDB активно развивается, и стратегии компактизации — одна из областей постоянно накатываемых изменений. Рассмотрим, что может ждать нас в будущих версиях ScyllaDB.
ML для выбора стратегии
Одно из направлений исследований — использование машинного обучения для автоматического выбора оптимальной стратегии компактизации. Система могла бы анализировать паттерны нагрузки и автоматически переключаться между стратегиями.
Пример: таблица использует STCS днем для эффективной записи и автоматически переключается на LCS ночью для оптимизации утренних запросов чтения (этакий "умный автономный контроллер"). Или система обнаруживает временной паттерн в данных и предлагает миграцию на TWCS.
Гибридные стратегии компактизации
Другое направление — создание гибридных стратегий, комбинирующих преимущества существующих подходов. ICS уже является примером такого подхода, объединяя простоту STCS с эффективностью использования пространства.
Будущие гибридные стратегии могут быть еще более сложными. Например, стратегия, которая использует TWCS для недавних данных и автоматически переключается на ICS для старых данных без TTL.
Компактизация с учетом особенностей SSD
Современные NVMe SSD имеют особенности, которые не полностью используются текущими стратегиями. Будущие стратегии могут учитывать:
Zoned Namespaces для более эффективного управления пространством
Multi-stream writes для лучшей производительности
Прямую интеграцию с SSD контроллерами
Заключение
Выбор и настройка стратегии компактизации — это не разовое решение, а постоянный процесс оптимизации. Раз и навсегда установить стратегию компактизации не получится. Каждая стратегия имеет свое место и время, свои сильные и слабые стороны.
STCS остается надежным выбором для многих сценариев, особенно при интенсивной записи. LCS непревзойденна для критически важных read-heavy нагрузок. TWCS — единственный разумный выбор для временных рядов с TTL. ICS представляет будущее компактизации, объединяя преимущества других подходов.
Важно помнить: никакая стратегия не может компенсировать плохой дизайн данных или неправильное использование базы данных. Компактизация — это мощный инструмент, но он работает лучше всего, когда вся система спроектирована с учетом ее особенностей.
Начните с понимания ваших данных и нагрузки. Выберите стратегию, которая лучше всего соответствует вашему use case. Мониторьте ключевые метрики. Будьте готовы изменить стратегию по мере роста и изменения вашей системы.
И самое главное — не нужно бояться экспериментировать со стратегиями.
Спасибо что дочитали до конца эту статью!
А если моя статья вам понравилась, то очень рекомендую подписаться на мой телеграм-канал "Данные по-большому" с материалами по инженерии данных (и не только по инженерии данных!), обзорами на компании и собеседования и мемами про IT и около-IT.
Полезные ресурсы:
ScyllaDB University: Полный курс по стратегиям компактизации
Презентация от разработчиков ScyllaDB о том, как выбор неправильной стратегии компактизации может критически повлиять на производительность.