ADG Оставить заявку
Блог Данные и аналитика 5 мин чтения

ClickHouse 25 LTS: переходим на нативный JSON и разбираемся с tiered storage на объектном хранилище

Обновили production-кластер до ClickHouse 25 LTS: делимся опытом перехода на нативный JSON-тип, замерами скорости и изменениями в tiered storage на объектном хранилище.

Контекст момента

ClickHouse 25 LTS - выход стабильного релиза с нативным JSON-типом и улучшенным tiered storage на объектных хранилищах

ClickHouse 25 LTS вышел, и мы достаточно быстро решили обновить production-кластер - не из желания быть на острие, а потому что два изменения в релизе касались нас напрямую: нативный JSON-тип и доработки tiered storage для объектных хранилищ. Оба пункта давно висели в нашем списке «хотим, но ждём стабильности».

Почему именно сейчас

Нативный JSON в ClickHouse существовал в экспериментальном виде несколько версий. Мы наблюдали за ним со стороны: feature-флаг, нестабильный DDL, периодические жалобы в issues на неожиданное поведение при сложных запросах. В LTS его перенесли в статус стабильного - это тот момент, когда мы переключаемся с «интересно» на «пробуем на production».

Tiered storage - наша давняя головная боль. Кластер у одного из заказчиков хранит аналитику событий за несколько лет, горячие данные живут на NVMe, холодные давно хотели переложить в S3-совместимое объектное хранилище. Прошлые версии tiered storage работали, но с рядом ограничений по поведению при merge и движению данных между уровнями, которые порождали странные ситуации в мониторинге.

Нативный JSON: что изменилось и зачем это нам

До 25 LTS мы хранили полуструктурированные данные стандартным способом - либо String с ручным JSONExtract*, либо Map(String, String) для плоских структур. Оба варианта работают, но с оговорками: JSONExtract тяжёлый при массовых запросах, Map не справляется с вложенностью.

Нативный тип JSON в 25 LTS хранит данные в колоночном виде внутри: каждый путь в JSON становится отдельной подколонкой. Это принципиально другой подход - не строка с парсингом на лету, а реальная колоночная структура, которая раскрывается при чтении.

Мы взяли тестовую таблицу с событиями, где payload - произвольный JSON от разных источников: примерно 30-40 регулярных полей и хвост редких. Сравнили три варианта на одном наборе данных:

  • String + JSONExtractString - наш текущий production-вариант.
  • JSON-тип в 25 LTS - новый нативный.
  • Набор Nullable(String)-колонок для часто используемых полей (то, что мы иногда делаем вручную для горячих путей).

На запросах, которые читают 3-5 конкретных путей из JSON по миллионам строк, нативный JSON выигрывает у String+JSONExtract заметно - разница в разы, не в процентах. Это ожидаемо: ClickHouse не парсит всю строку, а читает только нужные подколонки. На запросах, которые читают весь payload целиком (для дебага или экспорта), разница значительно меньше.

Интересный момент: схема в нативном JSON фиксируется не при создании таблицы, а при первом появлении пути в данных. Это удобно для источников с нестабильной структурой, но требует аккуратности - если один источник вдруг начнёт слать user_id как число, а другой как строку, ClickHouse попробует это разрешить, и результат может удивить. Мы добавили мониторинг на расхождение типов в system.columns через несколько дней после запуска - лучше поймать это до того, как оно проявится в запросе.

Tiered storage: что починили

Tiered storage в ClickHouse - это политики движения данных между дисками (disk policy). У нас конфигурация: hot (NVMe) -> warm (S3-совместимое хранилище). Движение происходит по TTL или по правилам заполненности диска.

До 25 LTS у нас периодически возникала ситуация: часть партов зависала в состоянии перемещения между уровнями, мерж между партами на разных уровнях вёл себя непредсказуемо, иногда оставляя временные куски на горячем диске дольше, чем должно. Это не разрушительно, но в мониторинге выглядело некрасиво и иногда давало ложные срабатывания по метрикам заполненности.

В 25 LTS команда Altinity и контрибьюторы довольно серьёзно поработали с механикой merge через уровни и с надёжностью метаданных на S3. После обновления поведение стало ощутимо чище: парты движутся предсказуемо, мерж не оставляет мусора на горячем уровне, метрики system.moves перестали показывать зависшие операции.

Конкретно нас ещё порадовала переработанная метрика MergedUncompressedBytes в разбивке по уровням - раньше для диагностики приходилось собирать её вручную из нескольких системных таблиц.

Как обновлялись

Обновление делали по одной реплике за раз - стандартная практика для кластера с репликацией. ClickHouse хорошо переносит rolling upgrade: пока одна реплика рестартует, остальные продолжают отвечать на запросы.

Единственный момент, который стоит учитывать при миграции на нативный JSON: существующие колонки типа String с JSON-содержимым сами по себе не мигрируют. Мы создали новые колонки с типом JSON, залили данные через INSERT ... SELECT с парсингом, поменяли источники данных. Это плановая работа, но её нужно заложить в scope заранее - «просто поменять тип ALTER-ом» не работает.

Где сейчас

Нативный JSON уже в production для двух таблиц с самой нестабильной схемой payload. Остальные пока остаются на String - не потому что плохо, а потому что миграция требует остановки источников на перенастройку, и это нужно координировать с командой заказчика. Tiered storage работает заметно спокойнее, мониторинг перестал шуметь.

Для DWH и аналитических задач с полуструктурированными данными нативный JSON в 25 LTS - реальное улучшение, а не маркетинг. Если у вас в ClickHouse живут данные с вариативной схемой и вы до сих пор разбираете их через JSONExtract, 25 LTS - хороший момент проверить, стоит ли переходить. Мы помогаем с аудитом и проектированием таких кластеров в рамках DWH/BI-проектов.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.