Партиции таблиц
Что такое партиции таблиц в ClickHouse?
Партиции группируют данные части таблицы в семействе MergeTree в организованные, логические единицы, что является способом организации данных, который концептуально имеет смысл и соответствует определенным критериям, таким как временные диапазоны, категории или другие ключевые атрибуты. Эти логические единицы облегчают управление, запросы и оптимизацию данных.
Partition By
Партиционирование можно включить при первоначальном определении таблицы через клаузу 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 миллисекунд: