Партиционирование#
Текущие поддерживаемые трансформации для партиций: https://iceberg.apache.org/spec/#partition-transforms
Рекомендации по выбору партиционирования данных#
На выбор типа партиционирования будут влиять ответы на следующие вопросы:
- Как будут использованы данные? Какой наиболее частый паттерн запросов (условие WHERE)?
- Какой объем данных?
- Какова кардинальность данных?
При ответе на эти вопросы также важно помнить о том, чтобы не допустить (или снизить к минимуму) возможный перекос в данных.
Крайне желательно, чтобы в данных была колонка с бизнес датой события, по которой в дальнейшем также можно настроить партиционирование.
Партиционирование по умолчанию#
Если НЕ удается узнать паттерн использования данных и по набору колонок также непонятно, что именно и как будет
селектиться, то в базовом варианте
можно использовать партиционирование по одной из технических колонок _stage_dt timestamp или _cleansed_dt timestamp.
Тип партиционирования (зависит от объема данных) один из:
- year
- month
- day
- hour
Выбор технической колонки#
- Если данные append-only и предполагается обрабатывать инкремент, то стоит использовать
_cleansed_dt. Значение этой колонки генерируется при вставке новых данных, поэтому оно всегда монотонно-возрастает и инкремент по такой колонке считать довольно просто. Недостатком является то, что любые повторные обработки данных также приведут к фактическому формированию нового инкремента, который нужно обработать. - Если по каким-то причинам важно даже при повторной обработке данных сохранить исходное распределение по партициям, то
стоит использовать
_stage_dt. Значением этой колонки является ingest_timestamp (время вставки данных в кафку). Для инкремента это НЕ подходит, так как потенциально консюмер МОЖЕТ обрабатывать разные партиции с разной скоростью.
Примеры данных с типами партиционирования#
Time-series#
Такие данные удобно партиционировать по бизнес-дате ивента: year(date) / month(date) / day(date) / hour(date). Выбор
конкретного уровня грануляции зависит от:
- Объема данных
- Предполагаемых запросов к данным
CREATE TABLE iceberg.domain.table
(
event_id string,
event_data string,
event_ts timestamp
) USING iceberg
PARTITIONED BY (days(event_ts));
[!caution] Также стоит учитывать потенциальное изменение/дозапись/дедупликация данных в прошлом. Если такая вероятность есть, то в некоторых случаях МОЖЕТ быть лучше выбрать более гранулярное партиционирование, чтобы перезапись партиции (в случае COW таблицы) было эффективнее.
Разбиение по категориям#
Если данные можно разбить по категориям и эти категории конечны (пример: статус, тип продукта, страна, etc), то в таких
случаях можно выбрать партиционирование с типом identity.
Дальнейший уровень разбиения зависит от паттернов доступа к данным и, если предполагается также фильтрация, например, по
дате, то имеет смысл следующим уровнем добавить партционирование по бизнес-дате:
CREATE TABLE iceberg.domain.table
(
data string,
category string,
ts timestamp
) USING iceberg
PARTITIONED BY (category, days(ts));
Фильтр по id#
В случае если есть необходимость доступа к данным по ключу с высокой кардинальностью, то стоит воспользоваться
партиционированием с типом bucket.
Число бакетов -- гипер-параметр, значение которого можно выбрать либо эмпирически, либо на основе знаний о данных.
Дальнейший уровень партиционирования также зависит от паттерна использования.
CREATE TABLE iceberg.domain.table
(
id string,
data string,
ts timestamp
) USING iceberg
PARTITIONED BY (bucket(N, id));
Разбиение по префиксам#
В некоторых случаях может быть полезен truncate. Одним из таких примеров является партиционирование событий по
префиксам урлов (урлы при этом нужно зеркально отражены)
CREATE TABLE iceberg.domain.table
(
event_id string,
url string,
ts timestamp
) USING iceberg
PARTITIONED BY (truncate(L, url));
Пример значений url:
Для URL подобный тип партиционирования МОЖЕТ работать, но нужно учитывать, что нормального разбиение партиций по доменам и суб-доменам не будет и такое партиционирование это в буквальном смысле просто substring(0, L).
CREATE TABLE iceberg.domain.table
(
event_id string,
url string,
status int,
ts timestamp
) USING iceberg
PARTITIONED BY (truncate(100, status));
В таком примере партиционирование по колонке status (если в статусе будут http-коды) появятся партиции:
Это может быть удобно для укрупненной группировки значений.
Эволюция настроек партиционирования#
Изменять настройки партиционирования МОЖНО. Новые данные будут партициорнированы по новым правилам. Существующие данные НЕ будут перепартиционированы по-новому. Для репартиционирования существующих данных нужно выполнить перезапись всех существующих файлов.