ClickHouse 25.5 и параллельный merge: тестируем на телеметрии IoT-датчиков
ClickHouse 25.5 получил параллельный merge партов и новые функции для временных рядов. Проверяем на реальной IoT-телеметрии: насколько легче ночные ETL-окна.
ClickHouse 25.5 с улучшенным параллельным слиянием партов и новыми функциями работы с временными рядами
ClickHouse 25.5 вышел в начале мая. Два изменения зацепили сразу: переработанный параллельный merge партов в MergeTree и набор новых функций для работы с временными рядами. У нас как раз есть стенд, где эти вещи можно проверить в условиях, близких к продуктиву, - IoT-телеметрия с нескольких сотен датчиков на производственном объекте клиента. Обновились и посмотрели.
Контекст: почему merge - это боль
Тем, кто не работал вплотную с MergeTree, поясним. ClickHouse при вставке данных не пишет их в одно место - он создаёт отдельные парты (data parts), которые потом фоново сливает (мёрджит) в более крупные. Это и есть основа высокой скорости вставки, но обратная сторона - фоновый процесс слияния постоянно жрёт ресурсы: CPU, I/O, память.
На IoT-стенде это особенно заметно. Датчики пишут данные равномерно весь день, но ночью клиент гоняет ETL-пайплайны: пересчёт агрегатов, заливка исторических поправок, пересборка материализованных представлений. В итоге ночное окно - это одновременно пик записи от ETL и пик фонового merge. Сервер в такие моменты вёл себя нервно: latency запросов росла, иногда срабатывал too many parts и вставки начинали тормозить.
Что изменилось в 25.5
До 25.5 merge в MergeTree был преимущественно однопоточным на уровне одного слияния. Параллелизм достигался запуском нескольких независимых merge-задач, но каждая из них внутри шла последовательно. В 25.5 движок научился распараллеливать чтение партов внутри одной задачи слияния - грубо говоря, читает несколько партов одновременно, а не поочерёдно.
Для машин с быстрыми NVMe и достаточным числом ядер это должно снизить время одного слияния и сократить накопление партов в очереди. На бумаге звучит хорошо. Проверяем на практике.
Стенд и методология
IoT-стенд: несколько сотен датчиков, данные пишутся каждые несколько секунд, общий объём таблицы телеметрии - несколько сотен миллионов строк, прирост около 50-80 млн строк в сутки в активный период. Сервер - 32-ядерная машина с NVMe-накопителями, RAM 128 ГБ. Версию подняли с 25.3 до 25.5 в тестовой среде с реальными данными.
Смотрели на три вещи:
- Количество партов в таблице в динамике за ночное окно - сколько успевает накопиться и как быстро схлопывается.
- Время выполнения ETL-задач - насколько быстрее или медленнее они проходят при параллельном merge.
- Latency SELECT-запросов во время активного merge - это то, что клиент ощущает напрямую.
Что наблюдали
Количество партов. На 25.3 в пик ночного ETL очередь партов разрасталась до нескольких сотен, и merge не успевал за вставками - типичная картина, когда прилетает много мелких батчей. На 25.5 та же нагрузка даёт заметно меньше накопившихся партов в пике: параллельное чтение внутри merge-задачи ускоряет схлопывание, и очередь не разрастается так сильно. До too many parts не дошли ни разу за несколько ночных прогонов.
ETL-задачи. Здесь сюрприз: задачи, которые делают много мелких вставок (заливка исторических поправок партиями по несколько тысяч строк), прошли немного быстрее. Задачи, которые пересчитывают агрегаты через INSERT INTO ... SELECT, особой разницы не показали - там узкое место не merge, а сам SELECT.
Latency SELECT. Заметное улучшение именно в моменты активного merge. Раньше в такие окна аналитические запросы по телеметрии иногда уходили за несколько секунд вместо привычных сотен миллисекунд. На 25.5 провалов стало меньше - merge не так агрессивно конкурирует за I/O с читающими запросами.
Новые функции для временных рядов
Параллельно с merge в 25.5 добавили несколько функций, специфичных для time series: seriesDecomposeSTL и seriesOutliersDetectTukey - разложение временного ряда на тренд/сезонность/остаток и детектор выбросов по методу Тьюки прямо в SQL.
Для IoT-телеметрии это интересно. Раньше для похожих задач мы тащили данные в Python, делали декомпозицию там, писали результат обратно. Теперь можно попробовать сделать это запросом. Успели только быстро пощупать: seriesOutliersDetectTukey на наших рядах работает предсказуемо, возвращает массив флагов выбросов. seriesDecomposeSTL - чуть медленнее, чем хотелось бы на длинных рядах, но для разовой аналитики вполне годится. Для потокового использования в дашборде смотреть надо отдельно.
Что пока не тестировали
Параллельный merge зависит от настроек - в частности, от max_threads и новых параметров, которые появились в 25.5. Мы пока гоняли на дефолтах, чтобы понять базовое поведение. Тюнинг под конкретные характеристики железа - следующий шаг.
Также не проверяли поведение на репликах. На одиночном узле результаты обнадёживающие, но у клиента в продуктиве кластер с несколькими репликами, и там параллельный merge может вести себя иначе из-за конкуренции задач между узлами.
Итог
Обновление на 25.5 в тестовой среде показало реальное улучшение именно в той точке, которая нас беспокоила - накопление партов и latency во время ночных ETL-окон. Параллельный merge работает, и на NVMe-железе с нормальным числом ядер разница ощутима.
На продуктив клиента переходить пока рано - хотим ещё прогнать с настройками, проверить на реплицируемом кластере. Но направление правильное. Для аналитики на ClickHouse с высокой частотой записи это одно из более полезных улучшений за последние несколько релизов.