Learning Platform
Глоссарий Troubleshooting
Урок 13.06 · 40 мин
Продвинутый
Apache IcebergMaintenanceCompactionSnapshot ExpiryOrphan CleanupREST CatalogPolarisNessieGravitinoLakekeepericeberg-rustSpec Versions

Обслуживание, экосистема и версии спецификации

Iceberg-таблица — живая система. Каждый коммит создаёт новый metadata file, manifest list, потенциально delete files. Без обслуживания таблица деградирует: растёт число мелких файлов, накапливаются устаревшие snapshots, orphan files занимают storage.

В этом уроке — три блока: обслуживание (snapshot expiry, orphan cleanup, compaction), версии спецификации (V1→V2→V3 и перспективы), экосистема каталогов и runtime (REST Catalog, Polaris, Nessie, Gravitino, Lakekeeper, iceberg-rust).

NOTE

Примеры кода — на Python с pyiceberg 0.11.1 (текущий релиз, март 2026) и Spark SQL (для maintenance procedures). Данные по экосистеме актуальны на март 2026.

Snapshot expiry

Каждый коммит создаёт новый snapshot. Snapshots иммутабельны и хранятся до явного удаления. Без expiry:

  • Metadata file растёт (массив snapshots[] + snapshot-log[])
  • Manifest lists и manifest files от старых snapshots занимают storage
  • Data files, которые были replaced/deleted, не удаляются физически

Механика

# pyiceberg: информация о snapshots
from pyiceberg.catalog import load_catalog

catalog = load_catalog("my_catalog")
table = catalog.load_table("db.orders")

# Число snapshots
snapshots = table.metadata.snapshots
print(f"Snapshots: {len(snapshots)}")
# → Snapshots: 1247 (после года CDC)

Expiry удаляет snapshots старше заданного порога и физически удаляет файлы, которые стали unreachable:

-- Spark: удалить snapshots старше 5 дней
CALL catalog.system.expire_snapshots(
 table => 'db.orders',
 older_than => TIMESTAMP '2026-03-22 00:00:00',
 retain_last => 10 -- оставить минимум 10 последних
);
Snapshot expiry: что удаляется
До expiryТаблица накопила 1247 snapshots за год CDC. Каждый snapshot ссылается на manifest list → manifest files → data files. Многие data files дублируются между snapshots (append → файл в нескольких snapshots).
Expiry processExpire snapshots older_than=5 дней, retain_last=10. Алгоритм: (1) найти все snapshots для удаления, (2) найти файлы, на которые ссылаются ТОЛЬКО удаляемые snapshots, (3) физически удалить файлы, (4) записать новый metadata file без expired snapshots.
После expiryОстаётся 10 snapshots. Файлы, на которые ссылались только удалённые snapshots, физически удалены с S3. Metadata file компактнее. Time travel ограничен последними 10 snapshots.
WARNING

Snapshot expiry безвозвратна. После expiry time travel к удалённым snapshots невозможен. Для compliance-данных используйте tags (иммутабельные ссылки на snapshot), чтобы защитить конкретные snapshots от expiry.

Автоматический expiry

-- Настроить auto-expiry через свойства таблицы
ALTER TABLE db.orders SET TBLPROPERTIES (
 'history.expire.max-snapshot-age-ms' = '432000000', -- 5 дней
 'history.expire.min-snapshots-to-keep' = '10'
);

Движки (Spark, Flink) проверяют эти свойства и автоматически запускают expiry при каждом коммите.

Orphan file cleanup

Orphan files — файлы на storage, на которые не ссылается ни один snapshot. Возникают при:

  1. Прерванных записях — writer начал писать data file, но упал до коммита
  2. Compaction race conditions — compaction переписал файлы, но старые не удалились
  3. Ошибках планирования — файл создан, но не добавлен в manifest
-- Spark: найти и удалить orphan files
CALL catalog.system.remove_orphan_files(
 table => 'db.orders',
 older_than => TIMESTAMP '2026-03-20 00:00:00',
 dry_run => true -- сначала посмотреть, потом удалять
);
Orphan file detection

Все файлы на storage

Шаг 1: собрать множество ВСЕХ файлов на storage (листинг S3/GCS). Это все .parquet, .avro, .metadata.json файлы в директории таблицы.
Файловая системаПолный листинг: data files, metadata files, manifest lists, manifest files, delete files, Puffin files. Включая файлы от прерванных записей.

Файлы из metadata

Шаг 2: собрать множество файлов, на которые ссылаются ALL snapshots (включая expired). Это все файлы, которые 'известны' Iceberg.
Referenced filesВсе файлы, упомянутые в manifest files всех snapshots. Эти файлы — 'живые'. Всё остальное — потенциальные orphans.
Разница = orphans
Orphan files50 файлов на storage, которые не упомянуты ни в одном snapshot. Обычно: прерванные записи, остатки compaction, артефакты ошибок. Безопасно удалить (если older_than достаточно большой).
DANGER

Всегда используйте older_than с запасом (минимум 24 часа). Без этого можно удалить файл, который writer ещё не закоммитил — операция записи занимает время, и файл на storage появляется раньше, чем commit в metadata.

Compaction (rewrite data files)

Small file problem — самая частая проблема производительности Iceberg-таблиц. Streaming-инъекция (Flink, Kafka) создаёт тысячи мелких файлов (1-10 MB вместо оптимальных 256-512 MB).

Bin-packing compaction

Объединяет мелкие файлы в крупные без изменения порядка данных:

-- Spark: bin-packing compaction
CALL catalog.system.rewrite_data_files(
 table => 'db.orders',
 strategy => 'binpack',
 options => map(
 'target-file-size-bytes', '268435456', -- 256 MB
 'min-file-size-bytes', '67108864', -- 64 MB (не трогать файлы > 64 MB)
 'max-file-size-bytes', '536870912', -- 512 MB
 'min-input-files', '5' -- минимум 5 файлов для merge
 )
);
Bin-packing: объединение мелких файлов

200 файлов × 5 MB = 1 GB

До compaction: 200 мелких файлов по 5 MB каждый = 1 GB данных в 200 файлах. Каждый file open, metadata read, Parquet footer parse — overhead на каждый файл.

4 файла × 256 MB = 1 GB

После bin-packing: 4 файла по 256 MB. Те же данные, но в оптимальных файлах. Меньше file opens, меньше metadata reads, лучше column chunk alignment в Parquet.

Sort-based compaction

Объединяет файлы и пересортировывает данные по указанным колонкам. Улучшает data skipping (min/max статистика плотнее):

CALL catalog.system.rewrite_data_files(
 table => 'db.orders',
 strategy => 'sort',
 sort_order => 'order_date ASC, region ASC',
 options => map('target-file-size-bytes', '268435456')
);

Z-ORDER compaction

Пересортировка по нескольким колонкам одновременно (Z-ORDER/Hilbert curve). Полезно, когда запросы фильтруют по разным комбинациям колонок:

CALL catalog.system.rewrite_data_files(
 table => 'db.orders',
 strategy => 'sort',
 sort_order => 'zorder(customer_id, order_date)',
 options => map('target-file-size-bytes', '268435456')
);
Стратегии compaction
Bin-packingОбъединяет мелкие файлы без пересортировки. Быстрее всего (только конкатенация). Не улучшает data skipping. Используйте как baseline-стратегию для streaming-таблиц.
Sort-basedОбъединяет + сортирует по 1-2 колонкам. Улучшает min/max статистику: файлы содержат непересекающиеся диапазоны → лучший data skipping. Дороже bin-packing (нужна сортировка).
Z-ORDERОбъединяет + Z-ORDER по нескольким колонкам. Оптимально для запросов с фильтрами по разным комбинациям (customer_id AND order_date, ИЛИ только customer_id, ИЛИ только order_date). Самый дорогой.

Rewrite delete files

V2 таблицы с множеством position delete files нужно компактировать:

-- Компактировать delete files
CALL catalog.system.rewrite_position_delete_files(
 table => 'db.orders'
);
TIP

В V3 с deletion vectors проблема delete file proliferation решена на уровне спецификации: writer обязан merge DVs. Отдельный rewrite_position_delete_files нужен только для V2 таблиц или после upgrade V2→V3.

Рекомендуемый maintenance pipeline

┌─────────────────────────────────────────────┐
│ Ежечасно (для streaming-таблиц): │
│ > rewrite_data_files (binpack, >5 files) │
│ > rewrite_position_delete_files (V2) │
├─────────────────────────────────────────────┤
│ Ежедневно: │
│ > expire_snapshots (older_than = 5 days) │
│ > remove_orphan_files (older_than = 3 days)│
├─────────────────────────────────────────────┤
│ Еженедельно: │
│ > rewrite_data_files (sort-based) │
│ > compute_partition_stats (incremental) │
└─────────────────────────────────────────────┘

Версии спецификации

V1 → V2 → V3

Эволюция спецификации Iceberg
V1 (оригинал)Оригинальная спецификация. Catalog-first архитектура, трёхуровневая иерархия метаданных, hidden partitioning, partition evolution, schema evolution с field IDs. Только Copy-on-Write. Нет row-level deletes.
V2 (current prod)Добавлены row-level deletes: position delete files и equality delete files. Sequence numbers для корректности. Branches и tags (refs). Текущая production-версия для большинства деплоев.
V3 (июнь 2025)Ратифицирован в июне 2025. Deletion vectors (Roaring bitmaps в Puffin), row lineage (_row_id, _last_updated_seq), default values для колонок, VARIANT тип (semi-structured), timestamp_ns/timestamptz_ns, геопространственные типы.

V3 feature mapping

FeatureV1V2V3
Metadata hierarchy
Hidden partitioning
Schema evolution (field ID)
Copy-on-Write
Position delete files! Deprecated (read-only)
Equality delete files
Sequence numbers
Branches/tags
Deletion vectors
Row lineage
Default column values
VARIANT type
Geospatial types
Nanosecond timestamps

Upgrade V2 → V3

-- Upgrade существующей таблицы
ALTER TABLE db.orders SET TBLPROPERTIES ('format-version' = '3');

Мгновенная операция — меняется только metadata. Существующие V2 position delete files продолжают читаться. Новые deletes пишутся как deletion vectors.

Будущее: V4

Обсуждения в community (по состоянию на март 2026):

  • Adaptive metadata tree — оптимизация metadata file для таблиц с миллионами partitions
  • Content-addressable metadata — дедупликация metadata files
  • Server-side scan planning — каталог планирует скан (REST API v2), клиент получает готовый план
  • Native indexing — встроенные индексы для point lookups
NOTE

V4 — на стадии обсуждений (DISCUSS в dev mailing list). Нет утверждённого feature set. Приведённые items — из публичных тредов community.

REST Catalog specification

Iceberg определяет стандартный REST API для каталогов. Любой каталог, реализующий этот API, совместим со всеми движками.

Ключевые endpoints

EndpointМетодОписание
/v1/namespacesGET/POSTList/create namespaces
/v1/namespaces/{ns}/tablesGET/POSTList/create tables
/v1/namespaces/{ns}/tables/{table}GET/DELETELoad/drop table
/v1/namespaces/{ns}/tables/{table}POSTUpdate table (commit)
/v1/namespaces/{ns}/tables/{table}/metricsPOSTReport metrics

OAuth2 и credential vending

REST Catalog поддерживает credential vending — каталог выдаёт временные credentials для доступа к storage:

REST Catalog: credential vending flow

Клиент → OAuth2 → Каталог

Клиент (Spark, Flink, pyiceberg) аутентифицируется в каталоге через OAuth2. Каталог проверяет RBAC: имеет ли клиент доступ к таблице. Если да — выдаёт временные S3/GCS credentials.
200 OK + credentials

Metadata + temp S3/GCS credentials

Каталог возвращает metadata таблицы + временные credentials для S3 (STS AssumeRole) или GCS (signed URLs). Credentials ограничены по времени и scope — только к файлам этой таблицы.
Direct access

Клиент → S3/GCS (direct read)

Клиент читает data files напрямую с S3/GCS используя полученные credentials. Каталог не проксирует данные — только metadata и auth. Минимальная нагрузка на каталог.
TIP

Credential vending — главное преимущество REST Catalog перед Hive Metastore. В Hive каждый клиент имеет полные AWS credentials. В REST — каталог выдаёт scoped временные credentials. Это zero-trust архитектура: клиент не видит master credentials.

Экосистема каталогов

Apache Polaris (incubating)

Reference implementation REST Catalog от Apache Software Foundation.

  • Версия: 1.3.0-incubating (январь 2026)
  • Runtime: Quarkus (Java 21+)
  • Storage backends: PostgreSQL, H2 (embedded), JDBC
  • Интеграции: Spark, Flink, Trino, DuckDB, Dremio, StarRocks, Apache Doris
  • Фичи: OAuth2, credential vending (AWS STS, GCS), RBAC, Generic Tables (GA в 1.3), Apache Ozone support
# Запуск Polaris
docker run -p 8181:8181 -p 8182:8182 apache/polaris:1.3.0-incubating

# Подключение из pyiceberg
from pyiceberg.catalog import load_catalog
catalog = load_catalog("polaris", **{
 "type": "rest",
 "uri": "http://localhost:8181/api/catalog",
 "credential": "CLIENT_ID:CLIENT_SECRET",
 "warehouse": "my_warehouse"
})
Экосистема каталогов Iceberg
Apache PolarisReference REST Catalog. Open-source (ASF incubating). Quarkus runtime, JDBC backends, OAuth2, credential vending. Generic Tables для Hudi/Delta. 1.3.0-incubating (январь 2026). Полная реализация REST API.
NessieGit-like версионирование для lakehouse. Branches, merges, commits для таблиц. Не просто каталог — version control для данных. Подходит для dev/staging/prod environments. Open-source (Dremio).
AWS GlueManaged каталог AWS. Serverless, интегрирован с EMR, Athena, Redshift Spectrum. Поддерживает Iceberg, Delta Lake, Hudi. Не полностью REST-совместим — AWS-специфичный API. V3 support (EMR 7.12).
GravitinoMetadata lake — каталог каталогов. Единый интерфейс для Iceberg, Hive, JDBC, Kafka. Уровень абстракции: один API для управления таблицами в разных системах. Apache incubating.
LakekeeperREST Catalog на Rust (построен поверх iceberg-rust). Apache License. Data access controls, OAuth2. Высокая производительность (native Rust). Подходит для edge / embedded сценариев.
Unity Catalog (OSS)Databricks open-sourced Unity Catalog. Поддерживает Delta Lake и Iceberg (через UniForm). Governance, lineage, AI/ML integration. REST API с расширениями для Delta.

Выбор каталога

КритерийPolarisNessieAWS GlueGravitinoLakekeeper
ТипREST CatalogVersion controlManaged serviceMeta-catalogREST Catalog
Self-hosted (AWS only)
Branching
Credential vending (IAM)
Multi-format (Generic Tables) (Iceberg focus) (Iceberg/Delta/Hudi) (любой) (Iceberg only)
RuntimeJava (Quarkus)Java (Quarkus)ServerlessJavaRust

Runtime-реализации

iceberg-rust 0.8.0

Нативная Rust-реализация спецификации Iceberg. Релиз 0.8.0 (январь 2026, текущий стабильный) — 144 PR от 37 контрибьюторов, охватывает работу с конца ноября 2025 по начало января 2026.

Ключевые возможности:

  • V3 metadata support — чтение и запись V3 manifest files с deletion vectors
  • DataFusion интеграция — INSERT INTO partitioned tables, partition column projection
  • Delete file support — position + equality deletes, shared caching
  • Каталоги — REST, Hive, Glue, SQL, S3 Tables, Memory
  • Writers — clustered и fanout writers для разных стратегий распределения
use iceberg::memory::MemoryCatalogBuilder;
use iceberg::{Catalog, CatalogBuilder, TableIdent};
use futures::TryStreamExt;

#[tokio::main]
async fn main() -> iceberg::Result<()> {
 let catalog = MemoryCatalogBuilder::default()
 .load("memory", /* config */)
 .await?;
 
 let table = catalog
 .load_table(&TableIdent::from_strs(["db", "orders"])?)
 .await?;
 
 let stream = table.scan()
 .select(["order_id", "amount"])
 .build()?
 .to_arrow()
 .await?;
 
 let batches: Vec<_> = stream.try_collect().await?;
 Ok(())
}

pyiceberg-core

Python-биндинги для iceberg-rust. Используются внутри pyiceberg для высокопроизводительного чтения:

# pyiceberg использует pyiceberg-core автоматически
# pip install pyiceberg[pyarrow] # включает pyiceberg-core
from pyiceberg.catalog import load_catalog

catalog = load_catalog("rest", uri="http://polaris:8181/api/catalog")
table = catalog.load_table("db.orders")

# Scan с filter pushdown — pyiceberg-core делает native Rust I/O
df = table.scan(
 row_filter="order_date >= '2026-01-01'",
 selected_fields=("order_id", "amount", "status")
).to_pandas()
Runtime-реализации Iceberg
Java (Apache Iceberg)Оригинальная reference implementation. Используется Spark, Flink, Trino, Hive. Полная поддержка V3. Самая зрелая реализация — все features спецификации.
Rust (iceberg-rust)Нативная Rust-реализация. 0.8.0 (январь 2026). V3 metadata, DataFusion, position/equality deletes. Python-биндинги через pyiceberg-core. Высокая производительность для embedded/edge.
Python (pyiceberg)Python SDK для Iceberg. Использует pyiceberg-core (Rust) для I/O, чистый Python для API. Schema evolution, table management, scan с filter pushdown. 0.11.1 (март 2026) — текущая stable.
C++ (iceberg-cpp)C++ реализация. 0.2.0 (начало 2026). Avro metadata, REST request/response models, expression framework. Ранняя стадия — ещё не production-ready. Для embedded C++ систем.
Go (iceberg-go)Go реализация в составе Arrow Go. Catalog API, scan, metadata parsing. Используется в Go-based системах (ClickHouse, custom pipelines).

Мониторинг таблицы

from pyiceberg.catalog import load_catalog

catalog = load_catalog("my_catalog")
table = catalog.load_table("db.orders")

# Общая статистика
metadata = table.metadata
print(f"Format version: {metadata.format_version}")
print(f"Snapshots: {len(metadata.snapshots)}")
print(f"Schemas: {len(metadata.schemas)}")
print(f"Partition specs: {len(metadata.partition_specs)}")
print(f"Sort orders: {len(metadata.sort_orders)}")
print(f"Current snapshot: {metadata.current_snapshot_id}")
print(f"Last column ID: {metadata.last_column_id}")
print(f"Location: {metadata.location}")

# Текущий snapshot details
snapshot = table.current_snapshot()
if snapshot:
 print(f"Snapshot ID: {snapshot.snapshot_id}")
 print(f"Sequence number: {snapshot.sequence_number}")
 print(f"Timestamp: {snapshot.timestamp_ms}")
 print(f"Operation: {snapshot.summary['operation']}")
 print(f"Total data files: {snapshot.summary.get('total-data-files', 'N/A')}")
 print(f"Total records: {snapshot.summary.get('total-records', 'N/A')}")
TIP

Для production monitoring: отслеживайте total-data-files и total-records в snapshot summary. Резкий рост total-data-files без пропорционального роста total-records — признак small file problem. Запустите compaction.

Итоги

  1. Snapshot expiry — удаляет старые snapshots и физически unreachable файлы. Настройте max-snapshot-age-ms и min-snapshots-to-keep
  2. Orphan cleanup — находит файлы на storage, не упомянутые в metadata. Всегда используйте older_than с запасом
  3. Compaction — bin-packing (мелкие → крупные), sort-based (улучшает data skipping), Z-ORDER (multi-column)
  4. V1→V2→V3: CoW only → position/equality deletes → deletion vectors (Roaring/Puffin) + row lineage + default values + VARIANT
  5. REST Catalog — стандартный API с OAuth2 и credential vending. Zero-trust архитектура
  6. Polaris 1.3.0 — reference REST Catalog (ASF incubating), Generic Tables GA, Ozone support
  7. iceberg-rust 0.8.0 (январь 2026) и PyIceberg 0.11.1 (март 2026) — V3 metadata, DataFusion, delete files. Python-биндинги через pyiceberg-core
  8. Maintenance pipeline: ежечасный binpack, ежедневный expiry + orphan cleanup, еженедельный sort compaction

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Iceberg-таблица: 500 snapshot, 200,000 data-файлов, ежедневный append + DELETE (MoR). expire_snapshots не запускался 6 месяцев. Какая последовательность maintenance операций оптимальна?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6