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

Слияния частей

Что такое слияния частей в ClickHouse?


ClickHouse быстрый не только для запросов, но и для вставок, благодаря своему слою хранения, который работает аналогично деревьям LSM:

① Вставки (в таблицы из семейства MergeTree engine) создают отсортированные, неизменяемые части данных.

② Вся обработка данных перегружена на фоновое слияние частей.

Это делает записи данных легковесными и высокоэффективными.

Чтобы контролировать количество ^^частей^^ на таблицу и реализовать ② выше, ClickHouse непрерывно сливает (по партициям) меньшие ^^части^^ в большие в фоновом режиме, пока они не достигнут сжатого размера примерно ~150 ГБ.

Следующая диаграмма иллюстрирует процесс фонового слияния:

PART MERGES

merge level части увеличивается на один с каждым дополнительным слиянием. Уровень 0 означает, что часть новая и еще не была слита. ^^Части^^, которые были слиты в большие ^^части^^, помечаются как неактивные и в конечном счете удаляются после настраиваемого времени (по умолчанию 8 минут). Со временем это создает дерево слитых ^^частей^^. Отсюда и название merge tree таблицы.

Мониторинг слияний

В примере что такое части таблицы мы показали, что 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 │
   └─────────────┴───────┴─────────┘

[Запуск] (https://sql.clickhouse.com/?query=U0VMRUNUCiAgICBuYW1lLAogICAgbGV2ZWwsCiAgICByb3dzCkZST00gc3lzdGVtLnBhcnRzCldIRVJFIChkYXRhYmFzZSA9ICd1aycpIEFORCAoYHRhYmxlYCA9ICd1a19wcmljZV9wYWlkX3NpbXBsZScpIEFORCBhY3RpdmUKT1JERVIgQlkgbmFtZSBBU0M7&run_query=true&tab=results) теперь показывает, что четыре ^^части^^ слились в одну финальную часть (если только в таблицу не было новых вставок):

   ┌─name───────┬─level─┬─────rows─┐
1. │ all_0_23_2 │     2 │ 25248433 │
   └────────────┴───────┴──────────┘

В ClickHouse 24.10 была добавлена новая панель мониторинга слияний в встроенные панели мониторинга. Доступная как в OSS, так и в облаке через HTTP-обработчик /merges, мы можем использовать ее для визуализации всех слияний частей для нашей таблицы примера:

PART MERGES

Записанная выше панель мониторинга захватывает весь процесс, начиная с начальных вставок данных и заканчивая финальным слиянием в одну часть:

① Количество активных ^^частей^^.

② Слияния частей, визуально представленные с помощью прямоугольников (размер отражает размер части).

Увеличение записи.

Параллельные слияния

Один сервер ClickHouse использует несколько фоновых потоков слияния для выполнения параллельных слияний частей:

PART MERGES

Каждый поток слияния выполняет цикл:

① Решает, какие ^^части^^ слить следующими, и загружает эти ^^части^^ в память.

② Сливает ^^части^^ в памяти в большую часть.

③ Записывает слитую часть на диск.

Вернуться к ①

Обратите внимание, что увеличение количества ядер CPU и объема ОЗУ позволяет увеличить пропускную способность фонового слияния.

Оптимизированные по памяти слияния

ClickHouse не обязательно загружает все ^^части^^, которые подлежат слиянию, в память одновременно, как это показано в предыдущем примере. На основе нескольких факторов, и для сокращения потребления памяти (пожертвовав скоростью слияния), так называемое вертикальное слияние загружает и сливает ^^части^^ по частям блоков, а не за один раз.

Механика слияния

Диаграмма ниже иллюстрирует, как один фоновый поток слияния в ClickHouse сливает ^^части^^ (по умолчанию, без вертикального слияния):

PART MERGES

Слияние частей выполняется в несколько этапов:

① Декодирование и загрузка: Сжатые бинарные файлы колонок из ^^частей^^, подлежащих слиянию, декодируются и загружаются в память.

② Слияние: Данные сливаются в более крупные файлы колонок.

③ Индексирование: Создается новый разреженный первичный индекс для слитых файлов колонок.

④ Сжатие и хранение: Новые файлы колонок и индекс сжимаются и сохраняются в новой директории, представляющей слитую часть данных.

Дополнительные метаданные в частях данных, такие как вторичные индексы пропуска данных, статистика по колонкам, контрольные суммы и мин-макс индексы, также восстанавливаются на основе слитых файлов колонок. Мы пропустили эти детали для простоты.

Механика этапа ② зависит от конкретного движка MergeTree, так как разные движки обрабатывают слияние по-разному. Например, строки могут быть агрегированы или заменены, если устарели. Как упоминалось ранее, этот подход перегружает всю обработку данных на фоновые слияния, позволяя супербыстрые вставки, поддерживая операции записи легковесными и эффективными.

В следующем разделе мы кратко опишем механику слияния конкретных движков в ^^MergeTree^^ семейства.

Стандартные слияния

Диаграмма ниже иллюстрирует, как ^^части^^ в стандартной MergeTree таблице сливаются:

PART MERGES

DDL-оператор на диаграмме выше создает таблицу MergeTree с ^^ключом сортировки^^ (town, street), что означает, что данные на диске отсортированы по этим колонкам, и создается соответствующий разреженный первичный индекс.

Сначала ① декомпрессированные, предварительно отсортированные колонки таблицы ② сливаются, сохраняя глобальный порядок сортировки таблицы, определяемый ^^ключом сортировки^^ таблицы, ③ создается новый разреженный первичный индекс, и ④ слитые файлы колонок и индекс сжимаются и сохраняются как новая часть данных на диске.

Заменяющие слияния

Слияния частей в таблице ReplacingMergeTree работают аналогично стандартным слияниям, но только самая последняя версия каждой строки сохраняется, а более старые версии отбрасываются:

PART MERGES

DDL-оператор на диаграмме выше создает таблицу ReplacingMergeTree с ^^ключом сортировки^^ (town, street, id), что означает, что данные на диске отсортированы по этим колонкам, с соответствующим разреженным первичным индексом.

Процесс ② слияния аналогичен слиянию в стандартной таблице MergeTree, объединяя декомпрессированные, предварительно отсортированные колонки при сохранении глобального порядка сортировки.

Тем не менее, ReplacingMergeTree удаляет дубликаты строк с одинаковым ^^ключом сортировки^^, сохраняя только самую последнюю строку на основе временной метки создания ее содержащей части.


Суммирующие слияния

Числовые данные автоматически суммируются во время слияний ^^частей^^ из таблицы SummingMergeTree:

PART MERGES

DDL-оператор на диаграмме выше определяет таблицу SummingMergeTree с town в качестве ^^ключа сортировки^^, что означает, что данные на диске отсортированы по этому столбцу, и создается соответствующий разреженный первичный индекс.

На этапе ② слияния ClickHouse заменяет все строки с одинаковым ^^ключом сортировки^^ на одну строку, суммируя значения числовых колонок.

Агрегационные слияния

Пример таблицы SummingMergeTree, упомянутый выше, является специализированным вариантом таблицы AggregatingMergeTree, позволяющим автоматическую инкрементальную трансформацию данных путем применения любой из 90+ агрегатных функций во время слияний частей:

PART MERGES

DDL-оператор на диаграмме выше создает таблицу AggregatingMergeTree с town в качестве ^^ключа сортировки^^, что гарантирует, что данные упорядочены по этому столбцу на диске, и создается соответствующий разреженный первичный индекс.

Во время ② слияния ClickHouse заменяет все строки с одинаковым ^^ключом сортировки^^ на одну строку, хранящую частичные состояния агрегации (например, sum и count для avg()). Эти состояния обеспечивают точные результаты через инкрементальные фоновые слияния.