Greptime:GreptimeDB Enterprise 现已集成 Apache Iceberg,支持从 Spark、Trino、DuckDB 和 pyiceberg 等引擎查询您的表,无需复制数据。

X AI KOLs Timeline 产品

摘要

GreptimeDB Enterprise 与 Apache Iceberg 集成,通过发布基于现有 Parquet 文件的 Iceberg 元数据,使表可以从 Spark、Trino、DuckDB 等引擎查询,无需复制数据。

GreptimeDB Enterprise 现已集成 Apache Iceberg,支持从 Spark、Trino、DuckDB 和 pyiceberg 等引擎查询您的表,无需复制数据。它发布引用对象存储中现有 Parquet 文件的 Iceberg 元数据,实现跨数据栈的无缝只读分析。 https://docs.greptime.com/enterprise/iceberg/… #GreptimeDB #ApacheIceberg
查看原文
查看缓存全文

缓存时间: 2026/09/15 19:51

GreptimeDB企业版现已与Apache Iceberg集成,使您的表可通过Spark、Trino、DuckDB和pyiceberg进行查询——无需复制数据。该集成会发布引用对象存储中现有Parquet文件的Iceberg元数据,实现跨数据栈的无缝只读分析。
https://docs.greptime.com/enterprise/iceberg/…
#GreptimeDB #ApacheIceberg


Iceberg导出 | GreptimeDB文档

来源:https://docs.greptime.com/enterprise/iceberg/

GreptimeDB企业版可通过Apache Iceberg(https://iceberg.apache.org/)REST目录服务暴露其表,使外部查询引擎——pyiceberg、Spark、Trino、DuckDB及其他兼容Iceberg的客户端——能直接通过标准Iceberg API从对象存储读取GreptimeDB数据。这是只读导出,而非数据复制或双写。GreptimeDB持续以原有方式写入数据;Iceberg集成仅发布元数据,供其他引擎定位并解析这些文件。

工作原理

核心思想在于GreptimeDB已将SST(排序字符串表)数据文件以Parquet格式存储于对象存储中。Iceberg集成不会重写、复制或导出数据,而是发布指向现有Parquet文件的Iceberg元数据——清单文件、清单列表和表元数据快照。任何支持Iceberg的引擎均可通过REST目录读取这些文件。下图展示了各组件如何协同工作:

该过程分布在您已运行的GreptimeDB进程间:

  • 数据节点/单机模式(写入端)。每当SST文件被写入(包括刷写、压缩、批量导入和截断操作),GreptimeDB会将当前Parquet文件集转换为Iceberg清单条目,并提交新的Iceberg快照。Iceberg元数据写入对象存储桶中warehouse_root前缀路径下,该存储桶与数据节点现有使用的是同一个。
  • 前端(目录服务器)。它实现了Iceberg REST目录API,挂载于/v1/iceberg路径,并通过直接从对象存储读取的方式向客户端提供各表的最新元数据——详见配置部分了解前端所需的对象存储设置。

由于数据文件从未被复制,因此不会产生额外存储成本或写入路径重复——导出仅是在GreptimeDB原有数据旁添加纯元数据。

GreptimeDB概念与Iceberg的映射关系

GreptimeDB概念Iceberg对应概念
Schema(数据库,如public)命名空间(Namespace)
表(Table)表(Table)
列(Column)字段(Field)
时间索引列timestamptz字段

通过ALTER TABLE进行的模式变更(添加/重命名/删除列)会实时反映在Iceberg模式中,无需重启服务。

类型映射

GreptimeDB列类型与Iceberg类型的对应关系如下:

GreptimeDB类型Iceberg类型
booleanboolean
int8、int16、int32int
int64long
float32float
float64double
stringstring
binarybinary
datedate
timestamp(任意精度)timestamptz
Prometheus原生直方图(结构体)struct(含list子字段)
uint8、uint16、uint32、uint64long——在Spark中不可读(见下文)
list、dictionary、json、interval、duration、time、任意structstring(有损回退)

配置

Iceberg集成是企业版插件。需在数据写入进程(数据节点或单机模式)和前端的[[plugins]]配置段中添加iceberg_manifest条目来启用:

  • 数据节点/单机模式运行发布Iceberg元数据的写入钩子。
  • 前端运行提供目录服务的REST目录。

两者必须引用相同的warehouse_root路径,确保目录读取的是写入端发布的元数据。

数据节点/单机模式。该进程已有用于数据存储的[storage]配置段——保持原样并添加插件条目:

## 数据节点已使用的对象存储(现有配置)
[storage]
type = "S3"
bucket = "greptimedb"
root = "greptimedb"
endpoint = "https://s3.us-east-1.amazonaws.com"
region = "us-east-1"
access_key_id = ""
secret_access_key = ""

## Iceberg清单导出发布Iceberg格式的元数据(快照、清单、清单列表),
## 使外部引擎(pyiceberg、Spark、Trino、DuckDB等)可通过Iceberg REST目录读取GreptimeDB表
[[plugins]]
iceberg_manifest = { warehouse_root = "iceberg_warehouse" }

前端。前端需要与数据节点相同的对象存储配置。REST目录在前端内部运行,并直接从对象存储读取Iceberg元数据——而非通过数据节点获取——因此若无[storage]配置段,目录无法解析任何表。前端通常自身没有[storage]配置段(仅代理查询);启用Iceberg集成正是其需要此配置的原因。请配置与数据节点相同的桶、root路径和凭证,并添加插件条目:

## 与数据节点相同的对象存储——前端的REST目录
## 从此处读取Iceberg元数据
[storage]
type = "S3"
bucket = "greptimedb"
root = "greptimedb"
endpoint = "https://s3.us-east-1.amazonaws.com"
region = "us-east-1"
access_key_id = ""
secret_access_key = ""

[[plugins]]
iceberg_manifest = { warehouse_root = "iceberg_warehouse" }

通过greptimedb-cluster Helm chart部署

在Kubernetes上无需手动编写前端的[storage]配置段。该chart通过objectStorage值集中配置对象存储,默认仅数据节点接收此配置。设置frontend.enableObjectStorage为true以使前端获得相同的对象存储配置,并通过frontend.configData添加插件条目:

frontend:
  ## 将objectStorage值(桶、root、凭证等)
  ## 注入前端作为其[storage]配置段
  enableObjectStorage: true
  configData: |
    [[plugins]]
    iceberg_manifest = { warehouse_root = "iceberg_warehouse" }

详见Helm chart配置(https://docs.greptime.com/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations/)了解objectStorage和configData字段。

插件选项如下:

选项默认值描述
warehouse_root"iceberg_warehouse"路径前缀,位于数据节点对象存储桶内,用于存储Iceberg元数据
enable_incrementaltrue设为true(默认)时,每次刷写/压缩/截断操作都会自动发布新快照。设为false可禁用自动发布,改为通过重建接口按需生成元数据;但删除/GC清理仍会执行

warehouse_root是上述对象存储桶内的路径前缀,会合并到存储的root中(如s3://bucket/root/...)——这就是为什么前端和数据节点必须在[storage]配置和warehouse_root上保持一致,否则目录会找不到元数据。

支持的对象存储后端包括S3、OSS、GCS和Azure Blob。启用后,REST目录可通过以下地址访问:

http://<frontend-host>:<port>/v1/iceberg

例如,若前端监听默认HTTP端口4000,则目录基础URI为http://localhost:4000/v1/iceberg,仓库名为greptime(默认值)。

使表可读

GreptimeDB在刷写时发布Iceberg元数据,因此新写入数据的导出会在下次刷写(或压缩)后生效。若要立即暴露刚导入的行,请手动刷写表:

-- 通过MySQL或PostgreSQL协议执行
admin flush_table('your_table');

元数据异步发布;刷写返回后不久,表即可通过REST目录查询。

已有表不会自动导出

自动发布仅涵盖插件启用后发生的清单更新(刷写、压缩、截断)——没有启动时的引导过程来发布此前已刷写的SST文件。因此,在集成启用前已刷写数据的表在下次刷写或压缩前,将不会出现在目录中。要导出此类已有表,请对每个表执行一次重建接口:

curl -X POST \
  "http://localhost:4000/v1/iceberg/v1/greptime/namespaces/public/tables/<table_name>/rebuild"

详见下文重建/协调部分了解重建的详细信息和注意事项。

使用pyiceberg读取

本示例使用pyiceberg和pyarrow通过REST目录读取GreptimeDB表。您需要安装pyiceberg和pyarrow,以及支撑GreptimeDB部署的对象存储凭证(此处以S3为例)。

from pyiceberg.catalog.rest import RestCatalog

# GreptimeDB前端暴露的REST目录端点
# `warehouse`是目录名称(默认目录为"greptime")
catalog = RestCatalog(
    name="greptime",
    uri="http://localhost:4000/v1/iceberg",
    prefix="greptime",
    # 对象存储凭证,使pyiceberg能读取Parquet数据文件
    # 请与GreptimeDB部署使用的后端匹配
    **{
        "s3.endpoint": "https://s3.us-east-1.amazonaws.com",
        "s3.access-key-id": "YOUR_ACCESS_KEY",
        "s3.secret-access-key": "YOUR_SECRET_KEY",
        "s3.region": "us-east-1",
    },
)

# 列出命名空间(= GreptimeDB模式)和表
print(catalog.list_namespaces())  # 例如 [('public',)]
print(catalog.list_tables("public"))  # 例如 [('public', 'my_table')]

# 加载表并使用pyarrow扫描
table = catalog.load_table(("public", "my_table"))
print(table.schema())  # Iceberg模式(GreptimeDB列 → 字段)
arrow_table = table.scan().to_arrow()
print(arrow_table.num_rows, "行")
print(arrow_table.to_pandas().head())

使用Spark读取

本示例配置Spark SQL使用GreptimeDB Iceberg REST目录,并对GreptimeDB表执行查询。您需要在Spark类路径中包含Iceberg Spark运行时和AWS bundle JAR文件(版本必须匹配您的Spark/Scala版本——此处以Spark 4.x配合Iceberg 1.11.0为例)。

1. spark-defaults.conf

spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
spark.sql.defaultCatalog=greptime

spark.sql.catalog.greptime=org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.greptime.type=rest
spark.sql.catalog.greptime.uri=http://localhost:4000/v1/iceberg
spark.sql.catalog.greptime.warehouse=greptime

# S3FileIO从GreptimeDB使用的同一对象存储读取Parquet数据文件
spark.sql.catalog.greptime.io-impl=org.apache.iceberg.aws.s3.S3FileIO
spark.sql.catalog.greptime.client.region=us-east-1
spark.sql.catalog.greptime.s3.endpoint=https://s3.us-east-1.amazonaws.com
spark.sql.catalog.greptime.s3.path-style-access=false
spark.sql.catalog.greptime.s3.access-key-id=YOUR_ACCESS_KEY
spark.sql.catalog.greptime.s3.secret-access-key=YOUR_SECRET_KEY

启动spark-sql(或spark-submit作业)时,需将两个Iceberg JAR文件加入类路径:

spark-sql \
  --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \
  --jars iceberg-spark-runtime-4.1_2.13-1.11.0.jar,iceberg-aws-bundle-1.11.0.jar

对于具有自定义端点和路径风格寻址的S3兼容存储(例如MinIO、Garage或本地测试部署),请将spark.sql.catalog.greptime.s3.endpoint设为存储URL,并将spark.sql.catalog.greptime.s3.path-style-access设为true。

2. 在GreptimeDB中创建并填充表
通过MySQL或PostgreSQL协议连接GreptimeDB,创建表并刷写以发布Iceberg元数据:

CREATE TABLE demo (
  ts TIMESTAMP(6) NOT NULL, -- 时间索引
  host STRING,
  region STRING,
  cpu DOUBLE,
  mem FLOAT,
  status INT,
  TIME INDEX (ts)
);

INSERT INTO demo VALUES
  ('2024-01-01 00:00:00', 'h1', 'us', 12.5, 4096.0, 200),
  ('2024-01-01 01:00:00', 'h1', 'us', 88.8, 7000.5, 200),
  ('2024-01-01 02:00:00', 'h2', 'eu', 55.0, 5500.0, 503);

-- 为刚写入的行发布Iceberg元数据
admin flush_table('demo');

3. 从Spark SQL查询

-- 目录为`greptime`,命名空间是GreptimeDB模式`public`
SHOW TABLES IN greptime.public;

SELECT count(*) FROM greptime.public.demo;

SELECT host, round(avg(cpu), 1) AS avg_cpu
FROM greptime.public.demo
WHERE ts >= '2024-01-01 00:00:00'
GROUP BY host
ORDER BY host;

该表可完全查询:时间范围过滤、聚合、连接、窗口函数和排序均可正常工作,与Spark中任何其他Iceberg表无异。

限制

只读、单一快照

  • 只读。您可以通过Iceberg读取GreptimeDB表,但不能通过Iceberg目录写回GreptimeDB。GreptimeDB仍是唯一写入端。
  • 仅最新快照(不支持时间旅行)。压缩操作会物理删除旧数据文件,因此历史快照不会保留。Iceberg表始终反映当前最新数据;无法查询先前快照或回滚。
  • 不保存完整模式历史。当前模式始终暴露,但不会保留历史模式版本,因此无法重构表在早期时间点的样貌。

数据类型与Spark

GreptimeDB类型与Iceberg类型在常见场景(布尔值、有符号整数、浮点数、字符串、二进制、日期、时间戳)下映射清晰。需特别注意以下几点,尤其是在Spark中:

  • 将时间索引声明为TIMESTAMP(6)。GreptimeDB的默认TIMESTAMP是毫秒精度,但Iceberg模式将列声明为timestamptz(微秒)。对于毫秒列,Spark的Parquet行组统计过滤会将微秒谓词与毫秒文件统计进行比较,可能在执行>、=和范围查询时错误地过滤掉行组。将时间索引声明为TIMESTAMP(6)可使磁盘上Parquet的微秒精度与模式匹配,所有比较运算符均可正常工作。(秒/毫秒值仍正确存储;问题仅在于对文件统计的谓词下推。)现有表无需重新创建:已声明为秒或毫秒精度的时间索引可通过ALTER TABLE就地扩展。此更改无损——现有数据以新单位读取,无需重写,新写入可使用更精细的精度:
ALTER TABLE demo MODIFY COLUMN ts TIMESTAMP_US;

不支持将单位缩回(例如从微秒改回毫秒),也不支持在使用metric引擎的表上进行扩展。详见ALTER TABLE了解详情。

  • 有损类型回退。list、dictionary、json、interval、duration、time和任意用户struct类型会作为Iceberg string导出,而非结构化类型,因此其内部结构无法通过Iceberg查询。

  • 无符号整数在Spark中不可读。GreptimeDB无符号整数列(uint8、uint16、uint32、uint64)被声明为Iceberg long,但底层Parquet文件使用无符号物理类型存储,Spark无法读取。任何触及无符号整数列的扫描

相似文章