Мы рекомендуем использовать Iceberg Table Function для работы с данными Iceberg в ClickHouse. В настоящее время Iceberg Table Function предоставляет достаточную функциональность, обеспечивая частичный интерфейс только для чтения для таблиц Iceberg.Iceberg Table Engine доступен, но может иметь ограничения. Изначально ClickHouse не разрабатывался для поддержки таблиц со схемами, которые изменяются извне, и это может влиять на функциональность Iceberg Table Engine. В результате некоторые возможности, доступные для обычных таблиц, могут быть недоступны или работать некорректно, особенно при использовании старого анализатора.Для максимальной совместимости мы рекомендуем использовать Iceberg Table Function, пока продолжаем улучшать поддержку Iceberg Table Engine.
Этот движок предоставляет доступ только для чтения к существующим таблицам Apache Iceberg в Amazon S3, Azure, HDFS и к локально хранимым таблицам.
Обратите внимание: таблица Iceberg уже должна существовать в хранилище; эта команда не принимает DDL-параметры для создания новой таблицы.
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])
Описание аргументов аналогично описанию аргументов для движков S3, AzureBlobStorage, HDFS и File.
format обозначает формат файлов данных в таблице Iceberg.
Для IcebergS3 можно использовать необязательный параметр extra_credentials, чтобы передать role_arn для доступа на основе ролей в ClickHouse Cloud. Инструкции по настройке см. в разделе Secure S3.
Параметры движка можно указать с помощью именованных коллекций
CREATE TABLE iceberg_table ENGINE=IcebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
Использование именованных коллекций:
<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>
CREATE TABLE iceberg_table ENGINE=IcebergS3(iceberg_conf, filename = 'test_table')
Движок таблицы Iceberg теперь служит псевдонимом для IcebergS3.
В следующей таблице показано, как типы данных Iceberg сопоставляются с типами данных ClickHouse при определении схемы (для чтения).
| Тип 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) | |
| Тип Iceberg | Тип ClickHouse |
|---|
list | Array |
map | Map |
struct | Tuple |
ClickHouse поддерживает чтение таблиц Iceberg, чья схема со временем менялась. Это относится к таблицам, в которых столбцы были добавлены, удалены или переставлены, а также к столбцам, изменённым с обязательных на Nullable. Кроме того, поддерживаются следующие приведения типов:
- int -> long
- float -> double
- decimal(P, S) -> decimal(P’, S) where P’ > P.
В настоящее время нельзя изменять вложенные структуры или типы элементов внутри массивов и Map.
Чтобы читать таблицу, в которой схема изменилась после создания с динамическим определением схемы, при её создании задайте allow_dynamic_metadata_for_data_lakes = true.
ClickHouse поддерживает отсечение партиций в запросах SELECT к таблицам Iceberg, что помогает повысить производительность запросов за счёт пропуска ненужных файлов данных. Чтобы включить отсечение партиций, установите use_iceberg_partition_pruning = 1. Дополнительные сведения об отсечении партиций в Iceberg см. по адресу https://iceberg.apache.org/spec/#partitioning
ClickHouse поддерживает путешествия во времени для таблиц Iceberg, что позволяет выполнять запросы к историческим данным по конкретной временной метке или идентификатору снимка.
Обработка таблиц с удалёнными строками
ClickHouse поддерживает чтение таблиц Iceberg, в которых используются следующие методы удаления:
Следующий метод удаления не поддерживается:
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_timestamp_ms = 1714636800000
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_snapshot_id = 3547395809148285433
Примечание: в одном и том же запросе нельзя указывать параметры iceberg_timestamp_ms и iceberg_snapshot_id одновременно.
-
Снимки обычно создаются, когда:
- В таблицу записываются новые данные
- Выполняется тот или иной вид компактации данных
-
Изменения схемы обычно не создают снимки — это приводит к важным особенностям при использовании time travel с таблицами, схема которых изменялась.
Все сценарии приведены для Spark, поскольку CH пока не поддерживает запись в таблицы Iceberg.
Сценарий 1: Изменения схемы без новых снимков
Рассмотрим следующую последовательность операций:
-- Создать таблицу с двумя столбцами
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
Сценарий 2: Различия между исторической и текущей схемой
Запрос time travel для текущего момента может показать схему, отличающуюся от схемы текущей таблицы:
-- Создать таблицу
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 из последнего файла метаданных, а не из снимка.
Сценарий 3: Различия между исторической и текущей схемой
Второй нюанс в том, что при использовании time travel невозможно получить состояние таблицы на момент, когда в неё ещё не было записано никаких данных:
-- Создать таблицу
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, и всё будет работать так же.
При использовании движка таблицы Iceberg в ClickHouse системе нужно определить правильный файл metadata.json, который описывает структуру таблицы Iceberg. Вот как работает этот процесс:
- Прямое указание пути:
- Если задан
iceberg_metadata_file_path, система использует именно этот путь, объединяя его с путем к каталогу таблицы Iceberg.
- Если задан этот параметр, все остальные настройки определения игнорируются.
- Сопоставление UUID таблицы:
- Если указан
iceberg_metadata_table_uuid, система будет:
- Рассматривать только файлы
.metadata.json в каталоге metadata
- Отбирать файлы, содержащие поле
table-uuid, совпадающее с указанным UUID (регистронезависимо)
- Поиск по умолчанию:
- Если ни один из указанных выше параметров не задан, кандидатами считаются все файлы
.metadata.json в каталоге metadata
Выбор самого нового файла
После того как по приведённым выше правилам определены файлы-кандидаты, система выбирает самый новый из них:
-
Если
iceberg_recent_metadata_file_by_last_updated_ms_field включён:
- Выбирается файл с наибольшим значением
last-updated-ms
-
В противном случае:
- Выбирается файл с наибольшим номером версии
- (Версия обозначается как
V в именах файлов формата V.metadata.json или V-uuid.metadata.json)
Примечание: Все упомянутые настройки (если явно не указано иное) относятся к уровню движка и должны быть заданы при создании таблицы, как показано ниже:
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, поэтому важно понимать эти правила разрешения.
Движок таблицы Iceberg и табличная функция Iceberg поддерживают кэширование данных, как и хранилища S3, AzureBlobStorage и HDFS. См. здесь.
Движок таблицы Iceberg и табличная функция поддерживают кэш метаданных, в котором хранится информация о файлах manifest, manifest list и JSON метаданных. Кэш хранится в памяти. Эта возможность управляется настройкой use_iceberg_metadata_files_cache, которая по умолчанию включена.
Асинхронную предварительную выборку метаданных можно включить при создании таблицы Iceberg, задав iceberg_metadata_async_prefetch_period_ms. Если задано значение 0 (по умолчанию) или если кэширование метаданных не включено, асинхронная предварительная выборка отключается.
Чтобы включить эту возможность, нужно указать ненулевое значение в миллисекундах. Оно задает интервал между циклами предварительной выборки.
Если возможность включена, сервер будет периодически выполнять фоновую операцию: опрашивать удаленный каталог и обнаруживать новую версию метаданных. Затем он разберет ее и рекурсивно пройдет по снимку, загружая активные файлы списков манифестов и файлы манифестов.
Файлы, уже доступные в кэше метаданных, не будут загружаться повторно. В конце каждого цикла предварительной выборки последний снимок метаданных будет доступен в кэше метаданных.
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 — не указано) в контексте каждого запроса сервер будет получать актуальные метаданные из удалённого каталога.
Если указать допустимую степень устаревания метаданных, сервер сможет использовать кэшированную версию снимка метаданных без обращения к удалённому каталогу. Если версия метаданных есть в кэше и была загружена в пределах заданного интервала устаревания, она будет использована для обработки запроса.
В противном случае из удалённого каталога будет получена последняя версия.
SELECT count() FROM icebench_table WHERE ...
SETTINGS iceberg_metadata_staleness_ms=120000
Примечание: Асинхронная предварительная загрузка метаданных выполняется в ICEBERG_SCEDULE_POOL, который представляет собой серверный пул потоков для фоновых операций с активными таблицами Iceberg. Размер этого пула потоков задается параметром конфигурации сервера iceberg_background_schedule_pool_size (по умолчанию — 10).
Примечание: В настоящее время предполагается, что размер кэша метаданных достаточен для полного хранения последнего снимка метаданных для всех активных таблиц, если включена асинхронная предварительная загрузка.
Последнее изменение 10 июня 2026 г.