Что такое слияния частей в ClickHouse?
ClickHouse работает быстро не только при выполнении запросов, но и при вставке данных благодаря своему уровню хранения, который устроен схожим образом с LSM-деревьями:
① Вставки (в таблицы семейства движков MergeTree) создают отсортированные неизменяемые части данных.
② Вся обработка данных переносится в фоновые слияния частей.
Это делает запись данных малозатратной и высокоэффективной.
Чтобы контролировать количество частей в таблице и реализовать описанное в пункте ② выше, ClickHouse непрерывно объединяет (в пределах каждой партиции) меньшие части в более крупные в фоновом режиме, пока их сжатый размер не достигнет примерно ~150 GB.
Следующая диаграмма схематично показывает этот фоновый процесс слияния:

уровень слияния части увеличивается на единицу с каждым очередным слиянием. Уровень 0 означает, что часть новая и еще не была объединена. Части, объединенные в более крупные, помечаются как неактивные и затем удаляются через настраиваемый промежуток времени (по умолчанию 8 минут). Со временем это образует дерево слитых частей. Отсюда и название таблицы MergeTree.
Мониторинг слияний
В примере что такое части таблицы мы показали, что ClickHouse отслеживает все части таблицы в системной таблице parts. Мы использовали следующий запрос, чтобы получить уровень слияния и число хранимых строк для каждой активной части таблицы из примера:
SELECT
name,
level,
rows
FROM system.parts
WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple') AND active
ORDER BY name ASC;Результат ранее приведённого запроса показывает, что в таблице из примера было четыре активные части, каждая из которых была создана в результате одного слияния изначально вставленных частей:
┌─name────────┬─level─┬────rows─┐
1. │ all_0_5_1 │ 1 │ 6368414 │
2. │ all_12_17_1 │ 1 │ 6442494 │
3. │ all_18_23_1 │ 1 │ 5977762 │
4. │ all_6_11_1 │ 1 │ 6459763 │
└─────────────┴───────┴─────────┘После запуска запроса теперь видно, что четыре части уже слились в одну итоговую часть (при условии, что в таблицу больше не выполняются вставки):
┌─name───────┬─level─┬─────rows─┐
1. │ all_0_23_2 │ 2 │ 25248433 │
└────────────┴───────┴──────────┘В ClickHouse 24.10 во встроенные панели мониторинга была добавлена новая панель мониторинга слияний. Она доступна как в OSS, так и в Cloud через HTTP-обработчик /merges и позволяет визуализировать все слияния частей для таблицы из нашего примера:

На записи панели мониторинга выше показан весь процесс — от первоначальных вставок данных до итогового слияния в одну часть:
① Количество активных частей.
② Слияния частей, визуально представленные прямоугольниками (их размер отражает размер части).
Параллельные слияния
Один сервер ClickHouse использует несколько фоновых потоков слияния для выполнения параллельных слияний частей:

Каждый поток слияния выполняет цикл:
Загрузка частей в память
Определяет, какие части сливать следующими, и загружает их в память.
Слияние частей
Объединяет части в памяти в одну более крупную часть.
Запись на диск
Записывает слитую часть на диск, затем возвращается к шагу 1.
Обратите внимание: увеличение числа ядер CPU и объема оперативной памяти позволяет повысить пропускную способность фоновых слияний.
Слияния с оптимизацией по памяти
ClickHouse не обязательно загружает в память все части, подлежащие слиянию, сразу, как показано в предыдущем примере. В зависимости от нескольких факторов и для снижения потребления памяти (ценой скорости слияния) так называемое вертикальное слияние загружает и сливает части фрагментами блоков, а не все сразу.
Механика слияния
Диаграмма ниже показывает, как один фоновый поток слияния в ClickHouse объединяет части (по умолчанию без вертикального слияния):

Слияние частей выполняется в несколько этапов:
Декомпрессия и загрузка
сжатые бинарные файлы столбцов из частей, подлежащих слиянию, распаковываются и загружаются в память.
Слияние
Данные сливаются в более крупные файлы столбцов.
Индексация
Для слитых файлов столбцов создаётся новый разреженный первичный индекс.
Дополнительные метаданные в частях данных, такие как вторичные индексы пропуска данных, статистика столбцов, контрольные суммы и min-max-индексы, также пересоздаются на основе слитых файлов столбцов. Для простоты мы опустили эти детали.
Механика этапа слияния зависит от конкретного движка MergeTree, поскольку разные движки выполняют слияние по-разному. Например, устаревшие строки могут агрегироваться или заменяться. Как уже упоминалось, такой подход переносит всю обработку данных в фоновые слияния, обеспечивая сверхбыстрые вставки за счёт того, что операции записи остаются лёгкими и эффективными.
Далее мы кратко рассмотрим механику слияния в конкретных движках семейства MergeTree.
Стандартные слияния
На схеме ниже показано, как выполняется слияние частей в стандартной таблице MergeTree:

DDL-оператор на схеме выше создаёт таблицу MergeTree с ключом сортировки (town, street), то есть данные на диске сортируются по этим столбцам, и на их основе создаётся разреженный первичный индекс.
① Распакованные предварительно отсортированные столбцы таблицы ② объединяются с сохранением глобального порядка сортировки, заданного ключом сортировки таблицы, ③ создаётся новый разреженный первичный индекс, а ④ объединённые файлы столбцов и индекс сжимаются и сохраняются на диске как новая часть данных.
Слияния в ReplacingMergeTree
Слияния частей в таблице ReplacingMergeTree работают аналогично стандартным слияниям, но сохраняется только самая новая версия каждой строки, а более старые версии отбрасываются:

DDL-оператор на диаграмме выше создаёт таблицу ReplacingMergeTree с ключом сортировки (town, street, id), то есть данные на диске сортируются по этим столбцам, а соответствующим образом строится разреженный первичный индекс.
На этапе ② слияние происходит так же, как и в стандартной таблице MergeTree: объединяются распакованные, предварительно отсортированные столбцы с сохранением общего порядка сортировки.
Однако ReplacingMergeTree удаляет дублирующиеся строки с одинаковым ключом сортировки, сохраняя только самую новую строку по временной метке создания части, в которой она находится.
Суммирующие слияния
Числовые данные автоматически суммируются при слиянии частей таблицы SummingMergeTree:

DDL-оператор на схеме выше определяет таблицу SummingMergeTree, где town используется как ключ сортировки. Это означает, что данные на диске сортируются по этому столбцу и на его основе создаётся разреженный первичный индекс.
На этапе слияния ② ClickHouse заменяет все строки с одинаковым ключом сортировки одной строкой, суммируя значения числовых столбцов.
Слияния с агрегацией
Пример таблицы SummingMergeTree, приведённый выше, — это специализированный вариант таблицы AggregatingMergeTree, который позволяет выполнять автоматическое инкрементальное преобразование данных, применяя любую из 90+ функций агрегации при слиянии частей:

DDL-оператор на диаграмме выше создаёт таблицу AggregatingMergeTree, в которой town используется как ключ сортировки, что гарантирует упорядочивание данных по этому столбцу на диске и создание соответствующего разреженного первичного индекса.
Во время ② слияния ClickHouse заменяет все строки с одинаковым ключом сортировки одной строкой, в которой хранятся Промежуточные состояния агрегации (например, sum и count для avg()). Эти состояния обеспечивают точные результаты благодаря инкрементальным фоновым слияниям.