Recomendamos usar a Iceberg Table Function para trabalhar com dados Iceberg no ClickHouse. No momento, a Iceberg Table Function oferece funcionalidade suficiente, fornecendo uma interface parcial somente leitura para tabelas Iceberg.O Iceberg Table Engine está disponível, mas pode ter limitações. O ClickHouse não foi originalmente projetado para oferecer suporte a tabelas com esquemas alterados externamente, o que pode afetar a funcionalidade do Iceberg Table Engine. Como resultado, alguns recursos que funcionam com tabelas comuns podem não estar disponíveis ou podem não funcionar corretamente, especialmente ao usar o analisador antigo.Para garantir a melhor compatibilidade, sugerimos usar a Iceberg Table Function enquanto continuamos a aprimorar o suporte ao Iceberg Table Engine.
Este motor oferece integração somente leitura com tabelas Apache Iceberg existentes no Amazon S3, Azure, HDFS e com tabelas armazenadas localmente.
Observe que a tabela Iceberg já deve existir no armazenamento; este comando não aceita parâmetros de DDL para criar uma nova tabela.
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])
A descrição dos argumentos coincide com a descrição dos argumentos dos motores S3, AzureBlobStorage, HDFS e File, respectivamente.
format indica o formato dos arquivos de dados na tabela Iceberg.
Para IcebergS3, é possível usar um parâmetro opcional extra_credentials para passar um role_arn para controle de acesso baseado em função no ClickHouse Cloud. Consulte S3 seguro para ver as etapas de configuração.
Os parâmetros do motor podem ser especificados usando Coleções nomeadas
CREATE TABLE iceberg_table ENGINE=IcebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
Usando coleções nomeadas:
<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')
O motor de tabela Iceberg agora é um alias para IcebergS3.
A tabela a seguir mostra como os tipos de dados do Iceberg são mapeados para os tipos de dados do ClickHouse durante a inferência de esquema (para leitura).
| Tipo do Iceberg | Tipo do ClickHouse | Observações |
|---|
boolean | Bool | |
int | Int32 | |
long, bigint | Int64 | |
float | Float32 | |
double | Float64 | |
date | Date32 | |
time | Int64 | Microssegundos desde a meia-noite |
timestamp | DateTime64(6) | Microssegundos, sem timezone |
timestamptz | DateTime64(6, 'UTC') | Microssegundos, timezone UTC |
timestamp_ns | DateTime64(9) | Nanossegundos, sem timezone (apenas no Iceberg v3 ou posterior) |
timestamptz_ns | DateTime64(9, 'UTC') | Nanossegundos, timezone UTC (apenas no Iceberg v3 ou posterior) |
string, binary | String | |
uuid | UUID | |
fixed(N) | FixedString(N) | |
decimal(P, S) | Decimal(P, S) | |
| Tipo do Iceberg | Tipo do ClickHouse |
|---|
list | Array |
map | Map |
struct | Tuple |
O ClickHouse oferece suporte à leitura de tabelas Iceberg cujo esquema evoluiu ao longo do tempo. Isso inclui tabelas em que colunas foram adicionadas, removidas ou reordenadas, bem como colunas que passaram de obrigatórias para Nullable. Além disso, há suporte para as seguintes conversões de tipo:
- int -> long
- float -> double
- decimal(P, S) -> decimal(P’, S) onde P’ > P.
No momento, não é possível alterar estruturas aninhadas nem os tipos dos elementos dentro de arrays e maps.
Para ler uma tabela cujo esquema foi alterado após sua criação com inferência dinâmica de esquema, defina allow_dynamic_metadata_for_data_lakes = true ao criar a tabela.
O ClickHouse oferece suporte à poda de partições durante consultas SELECT em tabelas Iceberg, o que ajuda a otimizar o desempenho das consultas ao ignorar arquivos de dados irrelevantes. Para habilitar a poda de partições, defina use_iceberg_partition_pruning = 1. Para mais informações sobre a poda de partições do Iceberg, acesse https://iceberg.apache.org/spec/#partitioning
O ClickHouse oferece suporte a viagem no tempo em tabelas Iceberg, permitindo consultar dados históricos usando um timestamp específico ou um ID de snapshot.
O ClickHouse oferece suporte à leitura de tabelas Iceberg que usam os seguintes métodos de exclusão:
O método de exclusão a seguir não é suportado:
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_timestamp_ms = 1714636800000
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_snapshot_id = 3547395809148285433
Observação: não é possível especificar os parâmetros iceberg_timestamp_ms e iceberg_snapshot_id na mesma consulta.
Considerações importantes
-
Snapshots normalmente são criados quando:
- Novos dados são gravados na tabela
- É realizada algum tipo de compactação de dados
-
Alterações de esquema normalmente não criam snapshots - Isso resulta em comportamentos importantes ao usar viagem no tempo com tabelas que passaram por evolução de esquema.
Todos os cenários estão em Spark porque o CH ainda não oferece suporte à gravação em tabelas Iceberg.
Cenário 1: Alterações de esquema sem novos snapshots
Considere esta sequência de operações:
-- Criar uma tabela com duas colunas
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
order_number int,
product_code string
)
USING iceberg
OPTIONS ('format-version'='2')
-- Inserir dados na tabela
INSERT INTO spark_catalog.db.time_travel_example VALUES
(1, 'Mars')
ts1 = now() // Um trecho de pseudocódigo
-- Alterar a tabela para adicionar uma nova coluna
ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)
ts2 = now()
-- Inserir dados na tabela
INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)
ts3 = now()
-- Consultar a tabela em cada 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|
+------------+------------+-----+
Resultados da consulta em diferentes timestamps:
- Em ts1 & ts2: aparecem apenas as duas colunas originais
- Em ts3: aparecem as três colunas, com NULL no preço da primeira linha
Cenário 2: Diferenças entre o esquema histórico e o atual
Uma consulta de viagem no tempo no momento atual pode mostrar um esquema diferente do esquema atual da tabela:
-- Criar uma tabela
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_2 (
order_number int,
product_code string
)
USING iceberg
OPTIONS ('format-version'='2')
-- Inserir dados iniciais na tabela
INSERT INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');
-- Alterar a tabela para adicionar uma nova coluna
ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);
ts = now();
-- Consultar a tabela no momento atual usando sintaxe de timestamp
SELECT * FROM spark_catalog.db.time_travel_example_2 TIMESTAMP AS OF ts;
+------------+------------+
|order_number|product_code|
+------------+------------+
| 2| Venus|
+------------+------------+
-- Consultar a tabela no momento atual
SELECT * FROM spark_catalog.db.time_travel_example_2;
+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
| 2| Venus| NULL|
+------------+------------+-----+
Isso acontece porque ALTER TABLE não cria um novo snapshot; para a tabela atual, o Spark obtém o valor de schema_id do arquivo de metadados mais recente, e não de um snapshot.
Cenário 3: Diferenças entre o esquema histórico e o atual
A segunda é que, ao usar a viagem no tempo, não é possível obter o estado da tabela antes de qualquer dado ter sido gravado nela:
-- Criar uma tabela
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();
-- Consultar a tabela em um timestamp específico
SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- Finaliza com erro: Cannot find a snapshot older than ts.
No ClickHouse, o comportamento é consistente com o do Spark. Você pode mentalmente substituir as consultas Select do Spark pelas consultas Select do ClickHouse, e isso funcionará da mesma forma.
Ao usar o motor de tabela Iceberg no ClickHouse, o sistema precisa localizar o arquivo metadata.json correto que descreve a estrutura da tabela Iceberg. Veja como esse processo de resolução funciona:
- Especificação direta do caminho:
- Se você definir
iceberg_metadata_file_path, o sistema usará esse caminho exato, combinando-o com o caminho do diretório da tabela Iceberg.
- Quando essa configuração é fornecida, todas as outras configurações de resolução são ignoradas.
- Correspondência do UUID da tabela:
- Se
iceberg_metadata_table_uuid for especificado, o sistema:
- Examinará apenas os arquivos
.metadata.json no diretório metadata
- Filtrará os arquivos que contêm um campo
table-uuid correspondente ao UUID especificado (sem diferenciar maiúsculas de minúsculas)
- Busca padrão:
- Se nenhuma das configurações acima for fornecida, todos os arquivos
.metadata.json no diretório metadata passam a ser candidatos
Seleção do arquivo mais recente
Depois de identificar os arquivos candidatos usando as regras acima, o sistema determina qual deles é o mais recente:
-
Se
iceberg_recent_metadata_file_by_last_updated_ms_field estiver habilitado:
- O arquivo com o maior valor de
last-updated-ms será selecionado
-
Caso contrário:
- O arquivo com o maior número de versão será selecionado
- (A versão aparece como
V em nomes de arquivo no formato V.metadata.json ou V-uuid.metadata.json)
Observação: Todas as configurações mencionadas (salvo indicação explícita em contrário) são configurações no nível da engine e devem ser especificadas durante a criação da tabela, como mostrado abaixo:
CREATE TABLE example_table ENGINE = Iceberg(
's3://bucket/path/to/iceberg_table'
) SETTINGS iceberg_metadata_table_uuid = '6f6f6407-c6a5-465f-a808-ea8900e35a38';
Observação: Embora os catálogos Iceberg normalmente façam a resolução de metadados, o mecanismo de tabela Iceberg do ClickHouse interpreta diretamente arquivos armazenados no S3 como tabelas Iceberg, por isso é importante entender essas regras de resolução.
O motor de tabela e a função de tabela Iceberg oferecem suporte a cache de dados, assim como S3, AzureBlobStorage e HDFS. Veja aqui.
O motor de tabela Iceberg e a função de tabela oferecem suporte a um cache de metadados que armazena informações dos arquivos de manifesto, da lista de manifestos e do JSON de metadados. O cache é armazenado em memória. Esse recurso é controlado pela configuração use_iceberg_metadata_files_cache, que vem habilitada por padrão.
A pré-busca assíncrona de metadados pode ser habilitada na criação da tabela Iceberg definindo iceberg_metadata_async_prefetch_period_ms. Se for definido como 0 (padrão) ou se o cache de metadados não estiver habilitado, a pré-busca assíncrona será desabilitada.
Para habilitar esse recurso, deve ser informado um valor diferente de zero, em milissegundos. Esse valor representa o intervalo entre os ciclos de pré-busca.
Se estiver habilitado, o servidor executará uma operação recorrente em segundo plano para listar o catálogo remoto e detectar uma nova versão dos metadados. Em seguida, ele fará a análise desses metadados e percorrerá recursivamente o snapshot, buscando arquivos ativos de lista de manifestos e arquivos de manifesto.
Os arquivos já disponíveis no cache de metadados não serão baixados novamente. Ao final de cada ciclo de pré-busca, o snapshot de metadados mais recente estará disponível no cache de metadados.
CREATE TABLE example_table ENGINE = Iceberg(
's3://bucket/path/to/iceberg_table'
) SETTINGS
iceberg_metadata_async_prefetch_period_ms = 60000;
Para aproveitar ao máximo a pré-busca assíncrona de metadados em operações de leitura, o parâmetro iceberg_metadata_staleness_ms deve ser especificado como parâmetro de consulta ou de sessão. Por padrão (0 - não especificado), no contexto de cada consulta, o servidor buscará os metadados mais recentes no catálogo remoto.
Ao especificar uma tolerância à defasagem dos metadados, o servidor pode usar a versão em cache do snapshot de metadados sem consultar o catálogo remoto. Se houver uma versão dos metadados no cache e ela tiver sido baixada dentro do intervalo de defasagem informado, ela será usada para processar a consulta.
Caso contrário, a versão mais recente será buscada no catálogo remoto.
SELECT count() FROM icebench_table WHERE ...
SETTINGS iceberg_metadata_staleness_ms=120000
Observação: A pré-busca assíncrona de metadados é executada no ICEBERG_SCEDULE_POOL, um threadpool do servidor para operações em segundo plano em tabelas Iceberg ativas. O tamanho desse threadpool é controlado pelo parâmetro de configuração do servidor iceberg_background_schedule_pool_size (o padrão é 10).
Observação: Atualmente, espera-se que o tamanho do cache de metadados seja suficiente para armazenar integralmente o snapshot de metadados mais recente de todas as tabelas ativas, se a pré-busca assíncrona estiver habilitada.
Última modificação em 10 de junho de 2026