Партиции таблиц
Что такое партиции таблиц в ClickHouse?
Партиции группируют части данных таблицы в семействе MergeTree в упорядоченные, логические единицы, что является способом организации данных, который имеет концептуальное значение и соответствует определённым критериям, таким как временные диапазоны, категории или другие ключевые атрибуты. Эти логические единицы упрощают управление, запрос и оптимизацию данных.
Partition By
Партиционирование может быть включено при первоначальном определении таблицы с помощью оператора PARTITION BY. Этот оператор может содержать SQL-выражение, основанное на любых колонках, результаты которого определяют, к какой партиции принадлежит строка.
Чтобы проиллюстрировать это, мы увеличиваем пример таблицы Что такое части таблиц, добавляя оператор PARTITION BY toStartOfMonth(date)
, который организует части данных таблицы на основе месяцев продаж недвижимости:
Вы можете запросить эту таблицу в нашем ClickHouse SQL Playground.
Структура на диске
Когда набор строк вставляется в таблицу, вместо создания (по крайней мере) одного единственного агрегированного данных (как описано здесь), 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
имеет более 600 партиций, и, следовательно, 600 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 миллисекунд: