> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-home-button.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

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

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

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

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

  Для максимальной совместимости мы рекомендуем использовать Iceberg Table Function, пока продолжаем улучшать поддержку 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](/ru/products/cloud/guides/data-sources/accessing-s3-data-securely).

Параметры движка можно указать с помощью [именованных коллекций](/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` теперь служит псевдонимом для `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="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 TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
  order_number int, 
  product_code string
  ) 
  USING iceberg 
  OPTIONS ('format-version'='2')

-- Вставить данные в таблицу
  INSERT INTO spark_catalog.db.time_travel_example VALUES 
    (1, 'Mars')

  ts1 = now() // Фрагмент псевдокода

-- Изменить таблицу, добавив новый столбец
  ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)
 
  ts2 = now()

-- Вставить данные в таблицу
  INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)

   ts3 = now()

-- Запросить таблицу для каждой временной метки
  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>

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

```sql theme={null}
-- Создать таблицу
  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 INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');

-- Изменить таблицу, добавив новый столбец
  ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);

  ts = now();

-- Запросить таблицу в текущий момент, используя синтаксис временной метки

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

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

-- Запросить таблицу в текущий момент
  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>

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

```sql theme={null}
-- Создать таблицу
  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();

-- Запросить таблицу на конкретную временную метку
  SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- Завершается с ошибкой: 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`. См. [здесь](/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](/ru/reference/functions/table-functions/iceberg)
