> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Этот движок предоставляет доступ только для чтения к существующим таблицам Apache Iceberg в Amazon S3, Azure, HDFS и к локально хранимым таблицам.

# Движок таблицы Iceberg

<Warning>
  Мы рекомендуем использовать [табличную функцию Iceberg](/docs/ru/reference/functions/table-functions/iceberg) для работы с данными Iceberg в ClickHouse. В настоящее время табличная функция Iceberg предоставляет достаточную функциональность, обеспечивая частичный интерфейс только для чтения для таблиц Iceberg.

  Iceberg Table Engine доступен, но может иметь ограничения. Изначально ClickHouse не разрабатывался для поддержки таблиц со схемами, которые изменяются извне, и это может влиять на функциональность Iceberg Table Engine. В результате некоторые возможности, доступные для обычных таблиц, могут быть недоступны или работать некорректно, особенно при использовании старого анализатора.

  Для максимальной совместимости мы рекомендуем использовать табличную функцию Iceberg, пока продолжаем улучшать поддержку Iceberg Table Engine.
</Warning>

Этот движок предоставляет интеграцию только для чтения с *данными* существующих таблиц Apache [Iceberg](https://iceberg.apache.org/) в Amazon S3, Azure, HDFS и локально хранимых таблиц.

<div id="create-table">
  ## CREATE TABLE
</div>

Обратите внимание: таблица Iceberg уже должна существовать в хранилище; эта команда не принимает DDL-параметры для создания новой таблицы.

```sql theme={null}
CREATE TABLE iceberg_table_s3
    ENGINE = IcebergS3(url, [, NOSIGN | access_key_id, secret_access_key, [session_token]], format, [,compression], [,extra_credentials])

CREATE TABLE iceberg_table_azure
    ENGINE = IcebergAzure(connection_string|storage_account_url, container_name, blobpath, [account_name, account_key, format, compression])

CREATE TABLE iceberg_table_hdfs
    ENGINE = IcebergHDFS(path_to_table, [,format] [,compression_method])

CREATE TABLE iceberg_table_local
    ENGINE = IcebergLocal(path_to_table, [,format] [,compression_method])
```

<div id="engine-arguments">
  ## Аргументы движка
</div>

Описание аргументов аналогично описанию аргументов для движков `S3`, `AzureBlobStorage`, `HDFS` и `File`.
`format` обозначает формат файлов данных в таблице Iceberg.

Для `IcebergS3` можно использовать необязательный параметр `extra_credentials`, чтобы передать `role_arn` для доступа на основе ролей в ClickHouse Cloud. Инструкции по настройке см. в разделе [Secure S3](/docs/ru/products/cloud/guides/data-sources/accessing-s3-data-securely).

Параметры движка можно указать с помощью [именованных коллекций](/docs/ru/concepts/features/configuration/server-config/named-collections)

<div id="example">
  ### Пример
</div>

```sql theme={null}
CREATE TABLE iceberg_table ENGINE=IcebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
```

Использование именованных коллекций:

```xml theme={null}
<clickhouse>
    <named_collections>
        <iceberg_conf>
            <url>http://test.s3.amazonaws.com/clickhouse-bucket/</url>
            <access_key_id>test</access_key_id>
            <secret_access_key>test</secret_access_key>
        </iceberg_conf>
    </named_collections>
</clickhouse>
```

```sql theme={null}
CREATE TABLE iceberg_table ENGINE=IcebergS3(iceberg_conf, filename = 'test_table')

```

<div id="aliases">
  ## Псевдонимы
</div>

Движок таблицы `Iceberg` автоматически определяет backend хранилища по настройке `disk` и соответственно направляет запросы в `IcebergS3`, `IcebergAzure` или `IcebergLocal`. Если `disk` не указан, по умолчанию используется реализация `IcebergS3`.

<div id="data-types">
  ## Типы данных
</div>

В следующей таблице показано, как типы данных Iceberg сопоставляются с типами данных ClickHouse при определении схемы (для чтения).

<div id="primitive-types">
  ### Примитивные типы
</div>

| Тип Iceberg        | Тип ClickHouse         | Примечания                                                   |
| ------------------ | ---------------------- | ------------------------------------------------------------ |
| `boolean`          | `Bool`                 |                                                              |
| `int`              | `Int32`                |                                                              |
| `long`, `bigint`   | `Int64`                |                                                              |
| `float`            | `Float32`              |                                                              |
| `double`           | `Float64`              |                                                              |
| `date`             | `Date32`               |                                                              |
| `time`             | `Int64`                | Микросекунды с полуночи                                      |
| `timestamp`        | `DateTime64(6)`        | Микросекунды, без часового пояса                             |
| `timestamptz`      | `DateTime64(6, 'UTC')` | Микросекунды, часовой пояс UTC                               |
| `timestamp_ns`     | `DateTime64(9)`        | Наносекунды, без часового пояса (только в Iceberg v3 и выше) |
| `timestamptz_ns`   | `DateTime64(9, 'UTC')` | Наносекунды, часовой пояс UTC (только в Iceberg v3 и выше)   |
| `string`, `binary` | `String`               |                                                              |
| `uuid`             | `UUID`                 |                                                              |
| `fixed(N)`         | `FixedString(N)`       |                                                              |
| `decimal(P, S)`    | `Decimal(P, S)`        |                                                              |

<div id="complex-types">
  ### Сложные типы
</div>

| Тип Iceberg | Тип ClickHouse |
| ----------- | -------------- |
| `list`      | `Array`        |
| `map`       | `Map`          |
| `struct`    | `Tuple`        |

<div id="schema-evolution">
  ## Эволюция схемы
</div>

ClickHouse поддерживает чтение таблиц Iceberg, чья схема со временем менялась. Это относится к таблицам, в которых столбцы были добавлены, удалены или переставлены, а также к столбцам, изменённым с обязательных на Nullable. Кроме того, поддерживаются следующие приведения типов:

* int -> long
* float -> double
* decimal(P, S) -> decimal(P', S) where P' > P.

В настоящее время нельзя изменять вложенные структуры или типы элементов внутри массивов и Map.

Чтобы читать таблицу, в которой схема изменилась после создания с динамическим определением схемы, при её создании задайте allow\_dynamic\_metadata\_for\_data\_lakes = true.

<div id="partition-pruning">
  ## Отсечение партиций
</div>

ClickHouse поддерживает отсечение партиций в запросах SELECT к таблицам Iceberg, что помогает повысить производительность запросов за счёт пропуска ненужных файлов данных. Чтобы включить отсечение партиций, установите `use_iceberg_partition_pruning = 1`. Дополнительные сведения об отсечении партиций в Iceberg см. по адресу [https://iceberg.apache.org/spec/#partitioning](https://iceberg.apache.org/spec/#partitioning)

<div id="time-travel">
  ## Путешествия во времени
</div>

ClickHouse поддерживает путешествия во времени для таблиц Iceberg, что позволяет выполнять запросы к историческим данным по конкретной временной метке или идентификатору снимка.

<div id="manifest-compaction">
  ## Уплотнение файлов манифеста
</div>

Со временем из-за частых операций записи в таблице Iceberg в списке файлов манифеста текущего снимка может накапливаться большое количество небольших файлов манифеста. Длинный список файлов манифеста замедляет планирование запроса, поскольку для обнаружения файлов данных нужно прочитать каждый файл манифеста. ClickHouse может уплотнить эти файлы манифеста, объединив их в меньшее число более крупных, с помощью оператора `OPTIMIZE TABLE ... MANIFEST`:

```sql theme={null}
OPTIMIZE TABLE example_table MANIFEST SETTINGS allow_experimental_iceberg_compaction = 1;
```

Это создает новый снимок (операция `replace`), который ссылается на те же файлы данных через консолидированный набор файлов манифестов. Файлы данных не перезаписываются, строки не добавляются, не удаляются и не дедуплицируются — перестраивается только слой манифестов.

<div id="manifest-compaction-behavior">
  ### Требования и поведение
</div>

* Эта возможность является экспериментальной и доступна только при включенной настройке `allow_experimental_iceberg_compaction`. Оператор генерирует исключение, если настройка не включена.
* Компакция выполняется только тогда, когда число файлов манифеста в списке манифестов текущего снимка превышает порог, заданный настройкой `iceberg_manifest_min_count_to_compact` (по умолчанию `30`). Если текущее число меньше или равно порогу, компакция пропускается и новый снимок не создается. Уменьшите порог, чтобы запускать компакцию при меньшем количестве файлов манифеста.
* `OPTIMIZE TABLE ... MANIFEST` поддерживается только для таблиц Iceberg. Попытка выполнить его для таблицы с любым другим движком генерирует исключение.
* `OPTIMIZE TABLE ... MANIFEST` поддерживается только для таблиц Iceberg формата версии 2. Выполнение для таблицы формата версии 1 генерирует исключение, как и выполнение для таблицы формата версии 3, поскольку для метаданных v3 row-lineage `first_row_id` пока не поддерживается корректное сохранение при переписывании манифеста.
* `OPTIMIZE TABLE ... MANIFEST` не поддерживается для зашифрованных таблиц Iceberg, чьи файлы данных содержат `key_metadata` на уровне отдельных файлов. Сохранение этих метаданных шифрования при переписывании манифеста пока не реализовано, поэтому оператор генерирует исключение `NOT_IMPLEMENTED`.

<div id="deleted-rows">
  ## Обработка таблиц с удалёнными строками
</div>

ClickHouse поддерживает чтение таблиц Iceberg, в которых используются следующие методы удаления:

* [Позиционное удаление](https://iceberg.apache.org/spec/#position-delete-files)
* [Удаление по равенству](https://iceberg.apache.org/spec/#equality-delete-files) (поддерживается начиная с версии 25.8+)

Следующий метод удаления **не поддерживается**:

* [Векторы удаления](https://iceberg.apache.org/spec/#deletion-vectors) (Добавленный в v3)

<div id="basic-usage">
  ### Базовое использование
</div>

```sql theme={null}
 SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_timestamp_ms = 1714636800000
```

```sql theme={null}
 SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_snapshot_id = 3547395809148285433
```

Примечание: в одном и том же запросе нельзя указывать параметры `iceberg_timestamp_ms` и `iceberg_snapshot_id` одновременно.

<div id="important-considerations">
  ### Важные замечания
</div>

* **Снимки** обычно создаются, когда:
  * В таблицу записываются новые данные
  * Выполняется тот или иной вид компактации данных

* **Изменения схемы обычно не создают снимки** — это приводит к важным особенностям при использовании time travel с таблицами, схема которых изменялась.

<div id="example-scenarios">
  ### Примеры сценариев
</div>

Все сценарии приведены для Spark, поскольку CH пока не поддерживает запись в таблицы Iceberg.

<div id="scenario-1">
  #### Сценарий 1: Изменения схемы без новых снимков
</div>

Рассмотрим следующую последовательность операций:

```sql theme={null}
-- Create a table with two columns
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
order_number int,
product_code string
  )
USING iceberg
OPTIONS ('format-version'='2')

-- Insert data into the table
INSERT INTO spark_catalog.db.time_travel_example VALUES
    (1, 'Mars')

  ts1 = now() // A piece of pseudo code

-- Alter table to add a new column
ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)

ts2 = now()

-- Insert data into the table
INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)

ts3 = now()

-- Query the table at each timestamp
  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts1;

+------------+------------+
|order_number|product_code|
+------------+------------+
|           1|        Mars|
+------------+------------+
  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts2;

+------------+------------+
|order_number|product_code|
+------------+------------+
|           1|        Mars|
+------------+------------+

  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts3;

+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
|           1|        Mars| NULL|
|           2|       Venus|100.0|
+------------+------------+-----+
```

Результаты запроса для разных временных меток:

* В ts1 & ts2: отображаются только два исходных столбца
* В ts3: отображаются все три столбца, при этом в столбце price для первой строки указано NULL

<div id="scenario-2">
  #### Сценарий 2: Различия между исторической и текущей схемой
</div>

Запрос путешествия во времени для текущего момента может показать схему, отличающуюся от схемы текущей таблицы:

```sql theme={null}
-- Create a table
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_2 (
order_number int,
product_code string
  )
USING iceberg
OPTIONS ('format-version'='2')

-- Insert initial data into the table
INSERT INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');

-- Alter table to add a new column
ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);

ts = now();

-- Query the table at a current moment but using timestamp syntax

  SELECT * FROM spark_catalog.db.time_travel_example_2 TIMESTAMP AS OF ts;

    +------------+------------+
    |order_number|product_code|
    +------------+------------+
    |           2|       Venus|
    +------------+------------+

-- Query the table at a current moment
  SELECT * FROM spark_catalog.db.time_travel_example_2;
    +------------+------------+-----+
    |order_number|product_code|price|
    +------------+------------+-----+
    |           2|       Venus| NULL|
    +------------+------------+-----+
```

Это происходит потому, что `ALTER TABLE` не создает новый снимок, а для текущей таблицы Spark берет значение `schema_id` из последнего файла метаданных, а не из снимка.

<div id="scenario-3">
  #### Сценарий 3: Различия между исторической и текущей схемой
</div>

Второй нюанс в том, что при использовании путешествия во времени невозможно получить состояние таблицы на момент, когда в неё ещё не было записано никаких данных:

```sql theme={null}
-- Create a table
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_3 (
order_number int,
product_code string
  )
USING iceberg
OPTIONS ('format-version'='2');

ts = now();

-- Query the table at a specific timestamp
  SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- Finises with error: Cannot find a snapshot older than ts.
```

В ClickHouse поведение такое же, как в Spark. Можете мысленно заменить запросы SELECT в Spark на запросы SELECT в ClickHouse, и всё будет работать так же.

<div id="metadata-file-resolution">
  ## Определение файла метаданных
</div>

При использовании движка таблицы `Iceberg` в ClickHouse системе нужно определить правильный файл `metadata.json`, который описывает структуру таблицы Iceberg. Вот как работает этот процесс:

<div id="candidate-search">
  ### Поиск кандидатов
</div>

1. **Прямое указание пути**:

* Если задан `iceberg_metadata_file_path`, система использует именно этот путь, объединяя его с путем к каталогу таблицы Iceberg.
* Если задан этот параметр, все остальные настройки определения игнорируются.

2. **Сопоставление UUID таблицы**:

* Если указан `iceberg_metadata_table_uuid`, система будет:
  * Рассматривать только файлы `.metadata.json` в каталоге `metadata`
  * Отбирать файлы, содержащие поле `table-uuid`, совпадающее с указанным UUID (регистронезависимо)

3. **Поиск по умолчанию**:

* Если ни один из указанных выше параметров не задан, кандидатами считаются все файлы `.metadata.json` в каталоге `metadata`

<div id="most-recent-file">
  ### Выбор самого нового файла
</div>

После того как по приведённым выше правилам определены файлы-кандидаты, система выбирает самый новый из них:

* Если `iceberg_recent_metadata_file_by_last_updated_ms_field` включён:
  * Выбирается файл с наибольшим значением `last-updated-ms`

* В противном случае:
  * Выбирается файл с наибольшим номером версии
  * (Версия обозначается как `V` в именах файлов формата `V.metadata.json` или `V-uuid.metadata.json`)

**Примечание**: Все упомянутые настройки (если явно не указано иное) относятся к уровню движка и должны быть заданы при создании таблицы, как показано ниже:

```sql theme={null}
CREATE TABLE example_table ENGINE = Iceberg(
    's3://bucket/path/to/iceberg_table'
) SETTINGS iceberg_metadata_table_uuid = '6f6f6407-c6a5-465f-a808-ea8900e35a38';
```

**Примечание**: Хотя каталоги Iceberg обычно отвечают за разрешение метаданных, движок таблицы `Iceberg` в ClickHouse напрямую интерпретирует файлы, хранящиеся в S3, как таблицы Iceberg, поэтому важно понимать эти правила разрешения.

<div id="data-cache">
  ## Кэш данных
</div>

Движок таблицы `Iceberg` и табличная функция `Iceberg` поддерживают кэширование данных, как и хранилища `S3`, `AzureBlobStorage` и `HDFS`. См. [здесь](/docs/ru/reference/engines/table-engines/integrations/s3#data-cache).

<div id="metadata-cache">
  ## Кэш метаданных
</div>

Движок таблицы `Iceberg` и табличная функция поддерживают кэш метаданных, в котором хранится информация о файлах manifest, manifest list и JSON метаданных. Кэш хранится в памяти. Эта возможность управляется настройкой `use_iceberg_metadata_files_cache`, которая по умолчанию включена.

<div id="async-metadata-prefetch">
  ## Асинхронная предварительная выборка метаданных
</div>

Асинхронную предварительную выборку метаданных можно включить при создании таблицы `Iceberg`, задав `iceberg_metadata_async_prefetch_period_ms`. Если задано значение 0 (по умолчанию) или если кэширование метаданных не включено, асинхронная предварительная выборка отключается.
Чтобы включить эту возможность, нужно указать ненулевое значение в миллисекундах. Оно задает интервал между циклами предварительной выборки.

Если возможность включена, сервер будет периодически выполнять фоновую операцию: опрашивать удаленный каталог и обнаруживать новую версию метаданных. Затем он разберет ее и рекурсивно пройдет по снимку, загружая активные файлы списков манифестов и файлы манифестов.
Файлы, уже доступные в кэше метаданных, не будут загружаться повторно. В конце каждого цикла предварительной выборки последний снимок метаданных будет доступен в кэше метаданных.

```sql theme={null}
CREATE TABLE example_table ENGINE = Iceberg(
    's3://bucket/path/to/iceberg_table'
) SETTINGS
    iceberg_metadata_async_prefetch_period_ms = 60000;
```

Чтобы максимально эффективно использовать асинхронную предзагрузку метаданных при операциях чтения, параметр `iceberg_metadata_staleness_ms` следует указывать как параметр запроса или сеанса. По умолчанию (0 — не указано) в контексте каждого запроса сервер будет получать актуальные метаданные из удалённого каталога.
Если указать допустимую степень устаревания метаданных, сервер сможет использовать кэшированную версию снимка метаданных без обращения к удалённому каталогу. Если версия метаданных есть в кэше и была загружена в пределах заданного интервала устаревания, она будет использована для обработки запроса.
В противном случае из удалённого каталога будет получена последняя версия.

```sql theme={null}
SELECT count() FROM icebench_table WHERE ...
SETTINGS iceberg_metadata_staleness_ms=120000
```

**Примечание**: Асинхронная предварительная загрузка метаданных выполняется в `ICEBERG_SCEDULE_POOL`, который представляет собой серверный пул потоков для фоновых операций с активными таблицами `Iceberg`. Размер этого пула потоков задается параметром конфигурации сервера `iceberg_background_schedule_pool_size` (по умолчанию — 10).

**Примечание**: В настоящее время предполагается, что размер кэша метаданных достаточен для полного хранения последнего снимка метаданных для всех активных таблиц, если включена асинхронная предварительная загрузка.

<div id="see-also">
  ## См. также
</div>

* [табличная функция Iceberg](/docs/ru/reference/functions/table-functions/iceberg)
