Перейти к содержанию

Партиционирование#

Текущие поддерживаемые трансформации для партиций: 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:

- com.mywebsite1.subdomain
- com.mywebsite2.subdomain1.subdomain2
- ru.mywebsite3

Для 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-коды) появятся партиции:

- 100
- 200
- 300
- 400
- 500

Это может быть удобно для укрупненной группировки значений.

Эволюция настроек партиционирования#

Изменять настройки партиционирования МОЖНО. Новые данные будут партициорнированы по новым правилам. Существующие данные НЕ будут перепартиционированы по-новому. Для репартиционирования существующих данных нужно выполнить перезапись всех существующих файлов.