В чем смысл MergeTree в ClickHouse и что мержится при вставке данных
Что проверяют
Понимаете ли вы, как ClickHouse хранит данные на диске, и не путаете ли части с партициями. Вопрос «что мержится при вставке» с подвохом: при вставке не мержится ничего, и интервьюер ждёт именно этого. Следом спрашивают про ошибку Too many parts и про дубли в ReplacingMergeTree.
Как отвечать
Каждый INSERT в таблицу MergeTree пишет новую часть (data part): отдельный каталог с данными, отсортированными по ключу ORDER BY. Данные хранятся по колонкам и сжимаются сразу при записи. Рядом строится разреженный первичный индекс: одна засечка на гранулу, по умолчанию 8192 строки (index_granularity). Часть неизменяема, поэтому вставка дешёвая: ничего существующего не переписывается.
Мерж идёт позже, в фоне. ClickHouse выбирает несколько частей одной партиции и сливает их в одну большую, как слияние отсортированных списков. Части из разных партиций не сливаются никогда. Во время мержа:
- уменьшается число частей, а значит, файлов и засечек, которые читает запрос;
- пересобираются индексы: первичный и индексы пропуска данных;
- срабатывает логика движка:
ReplacingMergeTreeоставляет одну строку на ключ сортировки,SummingMergeTreeсуммирует числовые колонки,AggregatingMergeTreeсворачивает состояния агрегатных функций,CollapsingMergeTreeсхлопывает пары строк с противоположнымsign.
Отсюда практика: вставлять крупными пачками, от тысяч строк за раз, либо включить async_insert, чтобы сервер копил мелкие вставки сам. PARTITION BY делать грубым, обычно по месяцу или дню, а не по пользователю.
Где спотыкаются
Называют части партициями. Партиция это логическая группа по PARTITION BY, внутри неё может быть много частей.
Думают, что ReplacingMergeTree гарантирует уникальность. Дубли живут до мержа, а когда он случится, неизвестно. Для точного результата нужен FINAL в запросе или агрегация через argMax, а OPTIMIZE TABLE ... FINAL по расписанию дорог и проблему не решает.
Шлют вставки по одной строке. Части плодятся быстрее, чем фоновые мержи их сливают, и таблица упирается в порог parts_to_throw_insert с ошибкой Too many parts.