Партиционирование доступно для таблиц семейства MergeTree, включая реплицируемые таблицы и materialized views.
Партиция — это логическое объединение записей в таблице по заданному критерию. Партицию можно задать по произвольному критерию, например по месяцу, дню или типу события. Каждая партиция хранится отдельно, что упрощает работу с этими данными. При обращении к данным ClickHouse использует минимально возможное подмножество партиций. Партиции повышают производительность запросов, содержащих ключ партиционирования, поскольку ClickHouse сначала отфильтровывает нужную партицию, а затем выбирает части и гранулы внутри неё.
Партиция задаётся в выражении PARTITION BY expr при создании таблицы. Ключ партиционирования может быть любым выражением на основе столбцов таблицы. Например, чтобы задать партиционирование по месяцам, используйте выражение toYYYYMM(date_column):
CREATE TABLE visits
(
VisitDate Date,
Hour UInt8,
ClientID UUID
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(VisitDate)
ORDER BY Hour;Ключ партиционирования также может представлять собой кортеж выражений (аналогично первичному ключу). Например:
ENGINE = ReplicatedCollapsingMergeTree('/clickhouse/tables/name', 'replica1', Sign)
PARTITION BY (toMonday(StartDate), EventType)
ORDER BY (CounterID, StartDate, intHash32(UserID));В этом примере мы задаём партиционирование по типам событий, произошедших на текущей неделе.
По умолчанию ключ партиционирования с числами с плавающей точкой не поддерживается. Чтобы использовать его, включите настройку allow_floating_point_partition_key.
При вставке новых данных в таблицу они сохраняются как отдельная часть (фрагмент), отсортированная по первичному ключу. Через 10–15 минут после вставки части одной и той же партиции сливаются в одну часть.
Используйте таблицу system.parts, чтобы посмотреть части таблицы и партиции. Например, предположим, что у нас есть таблица visits с партиционированием по месяцам. Выполним запрос SELECT к таблице system.parts:
SELECT
partition,
name,
active
FROM system.parts
WHERE table = 'visits'┌─partition─┬─name──────────────┬─active─┐
│ 201901 │ 201901_1_3_1 │ 0 │
│ 201901 │ 201901_1_9_2_11 │ 1 │
│ 201901 │ 201901_8_8_0 │ 0 │
│ 201901 │ 201901_9_9_0 │ 0 │
│ 201902 │ 201902_4_6_1_11 │ 1 │
│ 201902 │ 201902_10_10_0_11 │ 1 │
│ 201902 │ 201902_11_11_0_11 │ 1 │
└───────────┴───────────────────┴────────┘Столбец partition содержит имена партиций. В этом примере есть две партиции: 201901 и 201902. Значение этого столбца можно использовать, чтобы указать имя партиции в запросах ALTER … PARTITION.
Столбец name содержит имена частей данных партиции. Этот столбец можно использовать, чтобы указать имя части в запросе ALTER ATTACH PART.
Разберем имя части: 201901_1_9_2_11:
201901— это имя партиции.1— это минимальный номер блока данных.9— это максимальный номер блока данных.2— это уровень фрагмента (глубина дерева слияния, из которого он образован).11— это версия мутации (если к части была применена мутация)
Столбец active показывает статус части. 1 — активна; 0 — неактивна. Неактивные части — это, например, исходные части, оставшиеся после слияния в более крупную часть. Поврежденные части данных также помечаются как неактивные.
Как видно из примера, есть несколько отдельных частей одной и той же партиции (например, 201901_1_3_1 и 201901_1_9_2). Это означает, что эти части еще не слиты. ClickHouse периодически сливает вставленные части данных, примерно через 15 минут после вставки. Кроме того, вы можете выполнить внеплановое слияние с помощью запроса OPTIMIZE. Пример:
OPTIMIZE TABLE visits PARTITION 201902;┌─partition─┬─name─────────────┬─active─┐
│ 201901 │ 201901_1_3_1 │ 0 │
│ 201901 │ 201901_1_9_2_11 │ 1 │
│ 201901 │ 201901_8_8_0 │ 0 │
│ 201901 │ 201901_9_9_0 │ 0 │
│ 201902 │ 201902_4_6_1 │ 0 │
│ 201902 │ 201902_4_11_2_11 │ 1 │
│ 201902 │ 201902_10_10_0 │ 0 │
│ 201902 │ 201902_11_11_0 │ 0 │
└───────────┴──────────────────┴────────┘Неактивные части будут удалены примерно через 10 минут после слияния.
Ещё один способ посмотреть набор частей и партиций — перейти в каталог таблицы: /var/lib/clickhouse/data/<database>/<table>/. Например:
/var/lib/clickhouse/data/default/visits$ ls -l
total 40
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 1 16:48 201901_1_3_1
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 16:17 201901_1_9_2_11
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 15:52 201901_8_8_0
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 15:52 201901_9_9_0
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 16:17 201902_10_10_0
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 16:17 201902_11_11_0
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 16:19 201902_4_11_2_11
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 5 12:09 201902_4_6_1
drwxr-xr-x 2 clickhouse clickhouse 4096 Feb 1 16:48 detachedПапки '201901_1_1_0', '201901_1_7_1' и так далее — это каталоги частей. Каждая часть относится к соответствующей партиции и содержит данные только за определённый месяц (в этом примере в таблице используется партиционирование по месяцам).
Каталог detached содержит части, которые были отсоединены от таблицы с помощью запроса DETACH. Повреждённые части также перемещаются в этот каталог вместо удаления. Сервер не использует части из каталога detached. Вы можете в любой момент добавлять, удалять или изменять данные в этом каталоге — сервер не узнает об этом, пока вы не выполните запрос ATTACH.
Обратите внимание, что на работающем сервере нельзя вручную изменять состав частей или их данные в файловой системе, поскольку сервер об этом не узнает. Для нереплицируемых таблиц это можно сделать, когда сервер остановлен, но это не рекомендуется. Для реплицируемых таблиц состав частей нельзя изменять ни при каких обстоятельствах.
ClickHouse позволяет выполнять операции с партициями: удалять их, копировать из одной таблицы в другую или создавать резервную копию. Список всех операций см. в разделе Манипуляции с партициями и частями.
Оптимизация Group By с использованием ключа партиционирования
Для некоторых сочетаний ключа партиционирования таблицы и ключа группировки в запросе можно выполнять агрегацию по каждой партиции независимо. Тогда в конце не придётся объединять частично агрегированные данные из всех потоков выполнения, поскольку гарантируется, что каждое значение ключа группировки не может одновременно присутствовать в рабочих наборах двух разных потоков.
Типичный пример:
CREATE TABLE session_log
(
UserID UInt64,
SessionID UUID
)
ENGINE = MergeTree
PARTITION BY sipHash64(UserID) % 16
ORDER BY tuple();
SELECT
UserID,
COUNT()
FROM session_log
GROUP BY UserID;Ключевые факторы высокой производительности:
- число партиций, задействованных в запросе, должно быть достаточно большим (более
max_threads / 2), иначе запрос не будет в полной мере использовать ресурсы машины - партиции не должны быть слишком маленькими, чтобы батчевая обработка не выродилась в построчную
- партиции должны быть сопоставимы по размеру, чтобы все потоки выполняли примерно одинаковый объём работы
Соответствующие настройки:
allow_aggregate_partitions_independently- управляет тем, включено ли использование этой оптимизацииforce_aggregate_partitions_independently- принудительно включает её использование, когда это допустимо с точки зрения корректности, но она отключается внутренней логикой, оценивающей целесообразность её примененияmax_number_of_partitions_for_independent_aggregation- жёсткое ограничение на максимальное число партиций, которое может быть у таблицы