Разделы таблиц
Что такое разделы таблиц в ClickHouse?
Разделы группируют части данных таблицы в семействе MergeTree в организованные, логические единицы, которые являются способом организации данных, имеющим концептуальное значение и соответствующим определенным критериям, таким как диапазоны времени, категории или другие ключевые атрибуты. Эти логические единицы облегчают управление, выполнение запросов и оптимизацию данных.
Раздел по
Разделение может быть включено, когда таблица изначально определяется с помощью предложения PARTITION BY. Это предложение может содержать SQL-выражение на любых столбцах, результаты которого определяют, к какому разделу принадлежит строка.
Чтобы проиллюстрировать это, мы усиливаем пример таблицы Что такое части таблиц, добавляя предложение PARTITION BY toStartOfMonth(date)
, которое организует части данных таблицы на основе месяцев продаж недвижимости:
Вы можете выполнить запрос к этой таблице в нашем SQL Playground ClickHouse.
Структура на диске
Когда набор строк вставляется в таблицу, вместо создания (по крайней мере) одного единственного раздела данных, содержащего все вставленные строки (как описано здесь), ClickHouse создает новый раздел данных для каждого уникального значения ключа раздела среди вставленных строк:

Сервер ClickHouse сначала разбивает строки из примера вставки с 4 строками, нарисованными на диаграмме выше, по их значению ключа раздела toStartOfMonth(date)
. Затем для каждого идентифицированного раздела строки обрабатываются как обычно, выполняя несколько последовательных шагов (① Сортировка, ② Разделение на столбцы, ③ Сжатие, ④ Запись на диск).
Обратите внимание, что при включении разделения ClickHouse автоматически создает MinMax индексы для каждого раздела данных. Это просто файлы для каждого столбца таблицы, используемого в выражении ключа раздела, содержащие минимальные и максимальные значения этого столбца в пределах раздела данных.
Слияния по разделам
С включенным разделением ClickHouse объединяет только части данных внутри разделов, но не между ними. Мы это проиллюстрируем на примере нашей таблицы:

Как показано на диаграмме выше, части, принадлежащие различным разделам, никогда не объединяются. Если выбран ключ раздела с высокой кардинальностью, то части, распределенные по тысячам разделов, никогда не будут кандидатами на слияние - превышая заранее настроенные лимиты и вызывая ужасную ошибку Слишком много частей
. Решить эту проблему просто: выберите разумный ключ раздела с кардинальностью менее 1000..10000.
Мониторинг разделов
Вы можете выполнить запрос на список всех существующих уникальных разделов нашей примерной таблицы, используя виртуальный столбец _partition_value
:
Альтернативно, ClickHouse отслеживает все части и разделы всех таблиц в системной таблице system.parts, и следующий запрос возвращает для нашей примерной таблицы выше список всех разделов, плюс текущее количество активных частей и сумма строк в этих частях по разделу:
Для чего используются разделы таблиц?
Управление данными
В ClickHouse разделение в первую очередь является функцией управления данными. Организуя данные логически на основе выражения раздела, каждый раздел может управляться независимо. Например, схема разделения в приведенной выше примерной таблице позволяет сценариям, где только последние 12 месяцев данных сохраняются в основной таблице, автоматически удаляя старые данные с помощью правила TTL (см. добавленную последнюю строку оператора DDL):
Так как таблица разделена по toStartOfMonth(date)
, целые разделы (наборы частей таблиц), которые соответствуют условию TTL, будут удалены, что делает операцию очистки более эффективной, без необходимости переписывать части.
Аналогично, вместо удаления старых данных, их можно автоматически и эффективно перемещать на более доступный уровень хранения:
Оптимизация запросов
Разделы могут помочь с производительностью запросов, но это сильно зависит от шаблонов доступа. Если запросы нацелены только на несколько разделов (идеально - один), производительность может улучшиться. Это обычно полезно только в том случае, если ключ раздела не входит в первичный ключ и вы фильтруете по нему, как показано в примере запроса ниже.
Запрос выполняется над нашей примерной таблицей выше и вычисляет наивысшую цену всех проданных объектов недвижимости в Лондоне в декабре 2020 года, фильтруя как по столбцу (date
), использованному в ключе раздела таблицы, так и по столбцу (town
), использованному в первичном ключе таблицы (и date
не является частью первичного ключа).
ClickHouse обрабатывает этот запрос, применяя последовательность методов обрезки, чтобы избежать оценки неуместных данных:

① Обрезка по разделам: MinMax индексы используются для игнорирования целых разделов (наборов частей), которые логически не могут соответствовать фильтру запроса по столбцам, использованным в ключе раздела таблицы.
② Обрезка гранул: Для оставшихся частей данных после шага ① их первичный индекс используется для игнорирования всех гранул (блоков строк), которые логически не могут соответствовать фильтру запроса по столбцам, использованным в первичном ключе таблицы.
Мы можем наблюдать эти шаги обрезки данных, исследуя физический план выполнения запроса для нашего примерного запроса выше через предложение EXPLAIN:
Вывод выше показывает:
① Обрезка по разделам: Строки 7 по 18 вывода EXPLAIN выше показывают, что ClickHouse сначала использует MinMax индекс поля date
, чтобы определить 11 из 3257 существующих гранул (блоков строк), хранящихся в 1 из 436 существующих активных частей данных, которые содержат строки, соответствующие фильтру date
запроса.
② Обрезка гранул: Строки 19 по 24 вывода EXPLAIN выше указывают на то, что ClickHouse затем использует первичный индекс (созданный по полю town
) части данных, определенной на шаге ①, чтобы дополнительно сократить количество гранул (содержат строки, которые также могут соответствовать фильтру запроса по полю town
) с 11 до 1. Это также отражено в выводе ClickHouse-клиента, который мы напечатали чуть выше для выполненного запроса:
Что означает, что ClickHouse отсканировал и обработал 1 гранулу (блок 8192 строк) за 6 миллисекунд для вычисления результата запроса.
Разделение в первую очередь является функцией управления данными
Имейте в виду, что выполнение запросов через все разделы обычно медленнее, чем выполнение того же запроса на непартционированной таблице.
С разделением данные обычно распределяются по большему количеству частей данных, что часто приводит к тому, что ClickHouse сканирует и обрабатывает более объемные данные.
Мы можем продемонстрировать это, запустив один и тот же запрос как над таблицей Что такое части таблиц (без включенного разделения), так и над нашей текущей примерной таблицей выше (с включенным разделением). Обе таблицы содержат одинаковые данные и количество строк:
Однако таблица с включенным разделением, имеет больше активных частей данных, потому что, как было упомянуто выше, ClickHouse только сливает части данных внутри, но не между разделами:
Как показано выше, таблица с разделами uk_price_paid_simple_partitioned
имеет 306 разделов, и, следовательно, по крайней мере 306 активных частей данных. В то время как для нашей непартционированной таблицы uk_price_paid_simple
все инициальные части данных могут быть объединены в один активный раздел благодаря фоновой слиянии.
Когда мы проверяем физический план выполнения запроса с помощью предложения EXPLAIN для нашего примерного запроса выше без фильтрации по разделу, выполняемого над таблицей с разделами, мы можем увидеть в строках 19 и 20 вывода ниже, что ClickHouse идентифицировал 671 из 3257 существующих гранул (блоков строк), распределенных по 431 из 436 существующих активных частей данных, которые потенциально содержат строки, соответствующие фильтру запроса, и, следовательно, будут отсканированы и обработаны движком запросов:
Физический план выполнения запроса для того же примерного запроса, выполняемого над таблицей без разделов показывает в строках 11 и 12 вывода ниже, что ClickHouse идентифицировал 241 из 3083 существующих блоков строк внутри единой активной части данных таблицы, которые потенциально содержат строки, соответствующие фильтру запроса:
Для выполнения запроса над разделенной версией таблицы ClickHouse сканирует и обрабатывает 671 блока строк (~ 5.5 миллионов строк) за 90 миллисекунд:
В то время как для выполнения запроса над непартционированной таблицей ClickHouse сканирует и обрабатывает 241 блока (~ 2 миллиона строк) за 12 миллисекунд: