Spark 连接 Lakehouse(Iceberg REST)
概述
Lakehouse 提供了标准的 Apache Iceberg Catalog REST API 接口,允许外部计算引擎(如 Apache Spark)通过统一的 REST 协议访问和查询存储在 Lakehouse 中的 Iceberg 原生表。
核心特性
- 标准兼容:兼容 Apache Iceberg REST Catalog 规范
- 引擎支持:支持 Spark 3.5+ 计算引擎
- 权限委托:通过 vended-credentials 模式管理存储访问权限(仅阿里云支持)
- 云平台支持:当前仅支持阿里云 OSS
使用限制
表类型支持
| 表类型 | 是否支持 | 说明 |
|---|
| Lakehouse 原生 Iceberg 表 | ✅ 支持 | 完整读写,数据正确性已验证(阿里云实测确认) |
| 外部 Delta Lake 表 | ❌ 不支持 | REST Catalog 接口返回 Server error: Internal
Server error: Internal |
| 外部 Hudi 表 | ❌ 不支持 | 同上 |
| 外部 Lance 表 | ❌ 不支持 | 同上 |
| 外部 Paimon 表 | ❌ 不支持 | 同上 |
| Kafka 外部表 | ❌ 不支持 | 流式表无法通过 REST Catalog 扫描 |
数据类型限制
通过 Spark 访问时,以下数据类型暂不支持:
- 整数类型:
SMALLINT
SMALLINT
、TINYINT
TINYINT
- 半结构化类型:
JSON
JSON
- 向量类型:
VECTOR
VECTOR
腾讯云 COS 和 AWS S3 的 vended-credentials 限制
腾讯云和 AWS cn-north-1-aws 的 Lakehouse 实例 REST Catalog 接口可正常连接,元数据查询(SHOW NAMESPACES、SHOW TABLES、DESCRIBE TABLE)已验证可用,但均不支持 vended-credentials 模式:服务端加载表元数据时不返回
storage-credentials
storage-credentials
,导致 S3FileIO 无法获取临时存储凭证,数据读取失败。
如需通过 Spark 读取这两个云平台实例的数据,目前需要在客户端手动配置静态存储凭证:
- 腾讯云 COS:
spark.sql.catalog.cz.s3.access-key-id
spark.sql.catalog.cz.s3.access-key-id
/ spark.sql.catalog.cz.s3.secret-access-key
spark.sql.catalog.cz.s3.secret-access-key
- AWS S3:
spark.sql.catalog.cz.s3.access-key-id
spark.sql.catalog.cz.s3.access-key-id
/ spark.sql.catalog.cz.s3.secret-access-key
spark.sql.catalog.cz.s3.secret-access-key
阿里云 OSS 已通过 vended-credentials 完整验证,无需在客户端配置存储凭证。
环境要求
| 组件 | 版本要求 | 备注 |
|---|
| JDK | 17.x(必须) | Java 18+ 移除了 Subject.getSubject()
Subject.getSubject() ,导致 Hadoop 3.3 启动失败;Java 25/23 均无法使用 |
| Apache Spark | 3.5+ | 推荐 PySpark 3.5.5 |
| Python | 3.x | 用于 PySpark 驱动 |
| iceberg-spark-runtime | 1.6.1(Scala 2.12 / Spark 3.5.x) | Maven:org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1
org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1 |
| aliyun-sdk-oss | 3.18.1 | 阿里云专用:OSS 访问,Maven:com.aliyun.oss:aliyun-sdk-oss:3.18.1
com.aliyun.oss:aliyun-sdk-oss:3.18.1 |
| iceberg-aws-bundle | 1.6.1 | AWS 专用:包含 S3FileIO 及 AWS SDK,Maven:org.apache.iceberg:iceberg-aws-bundle:1.6.1
org.apache.iceberg:iceberg-aws-bundle:1.6.1 |
安装 PySpark
pip install "pyspark==3.5.5"
快速开始
连接参数说明
从 JDBC 连接串解析各配置字段:
jdbc:clickzetta://<instance_id>.<endpoint>.api.clickzetta.com/<workspace>
↑ instanceName ↑ region endpoint ↑ Workspace
REST Catalog URI 格式:
https://<endpoint>.api.clickzetta.com/api/v1/catalog/iceberg-rest
https://<endpoint>.api.clickzetta.com/api/v1/catalog/iceberg-rest
,
其他endpoint 取值请参考
文档
阿里云 PySpark 示例(OSSFileIO)
import os
import base64
from pyspark.sql import SparkSession
# Spark 3.5 必须使用 JDK 17(Java 18+ 不兼容 Hadoop 3.3)
os.environ["JAVA_HOME"] = os.path.expanduser("~/jdk17") # JDK 17 安装路径
os.environ["PATH"] = os.environ["JAVA_HOME"] + "/bin:" + os.environ.get("PATH", "")
# 指定 Maven 仓库(本地 .m2 缓存不完整时可避免依赖下载失败)
os.environ["PYSPARK_SUBMIT_ARGS"] = (
"--repositories https://repo1.maven.org/maven2 pyspark-shell"
)
# ── 认证 ────────────────────────────────────────────────────
USERNAME = "your_username" # Lakehouse 登录用户名
PASSWORD = "your_password" # Lakehouse 登录密码
auth_header = "Basic " + base64.b64encode(f"{USERNAME}:{PASSWORD}".encode()).decode()
# ── SparkSession ─────────────────────────────────────────────
spark = (
SparkSession.builder
.appName("CZ-Iceberg")
.config(
"spark.jars.packages",
"org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,"
"com.aliyun.oss:aliyun-sdk-oss:3.18.1", # 阿里云 OSS SDK
)
.config("spark.sql.extensions",
"org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
.config("spark.sql.defaultCatalog", "cz")
.config("spark.sql.catalog.cz", "org.apache.iceberg.spark.SparkCatalog")
.config("spark.sql.catalog.cz.type", "rest")
.config("spark.sql.catalog.cz.uri",
"https://<region_id>.api.clickzetta.com/api/v1/catalog/iceberg-rest") # 替换 <region_id>,参考下方区域端点对照表
.config("spark.sql.catalog.cz.header.instanceName", "<instance_id>") # JDBC URL 中主机名的第一段
.config("spark.sql.catalog.cz.header.Workspace", "<workspace>") # Workspace 名称
.config("spark.sql.catalog.cz.header.Authorization", auth_header)
.config("spark.sql.catalog.cz.header.X-Iceberg-Access-Delegation", "vended-credentials")
.config("spark.sql.catalog.cz.io-impl", "org.apache.iceberg.aliyun.oss.OSSFileIO")
.config("spark.sql.catalog.cz.oss.endpoint", "oss-cn-hangzhou.aliyuncs.com") # 替换为实例所在区域的 OSS endpoint
.config("spark.sql.catalog.cz.default-namespace", "public")
.getOrCreate()
)
AWS PySpark 示例(S3FileIO)
说明:AWS cn-north-1-aws 区域的 Iceberg REST Catalog 已开放,元数据查询(SHOW NAMESPACES/TABLES/DESCRIBE)已验证可用。但当前服务端不返回
storage-credentials
storage-credentials
,vended-credentials 模式暂不支持,数据读取需要在客户端配置静态 AWS 凭证(
s3.access-key-id
s3.access-key-id
/
s3.secret-access-key
s3.secret-access-key
)。
import os
import base64
from pyspark.sql import SparkSession
os.environ["JAVA_HOME"] = os.path.expanduser("~/jdk17") # JDK 17 安装路径
os.environ["PATH"] = os.environ["JAVA_HOME"] + "/bin:" + os.environ.get("PATH", "")
os.environ["PYSPARK_SUBMIT_ARGS"] = (
"--repositories https://repo1.maven.org/maven2 pyspark-shell"
)
USERNAME = "your_username" # Lakehouse 登录用户名
PASSWORD = "your_password" # Lakehouse 登录密码
auth_header = "Basic " + base64.b64encode(f"{USERNAME}:{PASSWORD}".encode()).decode()
spark = (
SparkSession.builder
.appName("CZ-Iceberg-AWS")
.config(
"spark.jars.packages",
"org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,"
"org.apache.iceberg:iceberg-aws-bundle:1.6.1", # 包含 S3FileIO 及 AWS SDK,无需单独配置静态凭证
)
.config("spark.sql.extensions",
"org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
.config("spark.sql.defaultCatalog", "cz")
.config("spark.sql.catalog.cz", "org.apache.iceberg.spark.SparkCatalog")
.config("spark.sql.catalog.cz.type", "rest")
.config("spark.sql.catalog.cz.uri",
"https://<region_id>.api.clickzetta.com/api/v1/catalog/iceberg-rest") # 替换 <region_id>,参考下方区域端点对照表
.config("spark.sql.catalog.cz.header.instanceName", "<instance_id>") # JDBC URL 中主机名的第一段
.config("spark.sql.catalog.cz.header.Workspace", "<workspace>") # Workspace 名称
.config("spark.sql.catalog.cz.header.Authorization", auth_header)
.config("spark.sql.catalog.cz.header.X-Iceberg-Access-Delegation", "vended-credentials")
.config("spark.sql.catalog.cz.io-impl", "org.apache.iceberg.aws.s3.S3FileIO") # AWS S3 FileIO
.config("spark.sql.catalog.cz.default-namespace", "public")
.getOrCreate()
)
基本查询操作
# 查看所有 schema
spark.sql("SHOW NAMESPACES IN cz").show()
# 查看表列表
spark.sql("SHOW TABLES IN cz.public").show(100, truncate=False)
# 查看表结构
spark.sql("DESCRIBE TABLE cz.public.your_table").show()
# 查询数据
spark.sql("SELECT * FROM cz.public.your_table LIMIT 10").show()
# DataFrame API
df = spark.table("cz.public.your_table")
df.filter("amount > 100").select("id", "amount").show()
配置参数详解
| 参数 | 参数描述 | 示例值 | 是否必须 |
|---|
spark.jars.packages
spark.jars.packages | 指定 Spark 启动时从 Maven 自动下载的依赖包,包含 Iceberg Spark runtime 和云对象存储 SDK | 阿里云:org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,com.aliyun.oss:aliyun-sdk-oss:3.18.1
org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,com.aliyun.oss:aliyun-sdk-oss:3.18.1 AWS:org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,org.apache.iceberg:iceberg-aws-bundle:1.6.1
org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.6.1,org.apache.iceberg:iceberg-aws-bundle:1.6.1 | 是 |
spark.sql.extensions
spark.sql.extensions | 向 Spark SQL 注入 Iceberg 扩展,使其能解析 Iceberg 特有的 DDL/DML 语句 | org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions | 是 |
spark.sql.defaultCatalog
spark.sql.defaultCatalog | 设置 Spark SQL 的默认 Catalog,设置后 SQL 中可省略 catalog 前缀 | cz
cz | 否 |
spark.sql.catalog.cz
spark.sql.catalog.cz | 注册名为 cz
cz 的 Iceberg Catalog,指定实现类 | org.apache.iceberg.spark.SparkCatalog
org.apache.iceberg.spark.SparkCatalog | 是 |
spark.sql.catalog.cz.type
spark.sql.catalog.cz.type | 指定 Catalog 类型为 REST,通过 HTTP 与远端元数据服务通信 | rest
rest | 是 |
spark.sql.catalog.cz.uri
spark.sql.catalog.cz.uri | REST Catalog 服务的 API 端点,Spark 向此地址发送所有元数据请求 | https://cn-hangzhou-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest
https://cn-hangzhou-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest | 是 |
spark.sql.catalog.cz.header.instanceName
spark.sql.catalog.cz.header.instanceName | HTTP 请求头,用于标识目标 Lakehouse 实例(JDBC URL 中主机名的第一段) | be6213f1
be6213f1 | 是 |
spark.sql.catalog.cz.header.Workspace
spark.sql.catalog.cz.header.Workspace | HTTP 请求头,指定要操作的 Workspace 名称 | quick_start
quick_start | 是 |
spark.sql.catalog.cz.header.Authorization
spark.sql.catalog.cz.header.Authorization | HTTP 认证头,值为 Basic <base64(username:password)>
Basic <base64(username:password)> | Basic R19ZVU5RSTo=...
Basic R19ZVU5RSTo=... | 是 |
spark.sql.catalog.cz.header.X-Iceberg-Access-Delegation
spark.sql.catalog.cz.header.X-Iceberg-Access-Delegation | 启用 Vended Credentials 模式,由 Catalog 服务下发临时存储凭证,避免在客户端暴露长期密钥 | vended-credentials
vended-credentials | 是 |
spark.sql.catalog.cz.io-impl
spark.sql.catalog.cz.io-impl | 指定读写数据文件(Parquet 等)的 FileIO 实现。阿里云使用 OSSFileIO,AWS 使用 S3FileIO | 阿里云:org.apache.iceberg.aliyun.oss.OSSFileIO
org.apache.iceberg.aliyun.oss.OSSFileIO AWS:org.apache.iceberg.aws.s3.S3FileIO
org.apache.iceberg.aws.s3.S3FileIO | 是 |
spark.sql.catalog.cz.oss.endpoint
spark.sql.catalog.cz.oss.endpoint | 阿里云专用。OSS 区域端点,客户端通过此地址访问存储桶中的数据文件(参考文档) | oss-cn-hangzhou.aliyuncs.com
oss-cn-hangzhou.aliyuncs.com | 阿里云必须 |
spark.sql.catalog.cz.default-namespace
spark.sql.catalog.cz.default-namespace | Catalog 内的默认命名空间(Schema),未显式指定时的默认操作目标 | public
public | 否(推荐) |
区域端点对照
| 实例区域 | REST Catalog URI | 存储 Endpoint | 状态 |
|---|
| 阿里云 华东1(杭州) | https://cn-hangzhou-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest
https://cn-hangzhou-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest | oss-cn-hangzhou.aliyuncs.com
oss-cn-hangzhou.aliyuncs.com | ✅ 已验证 |
| 阿里云 华东2(上海) | https://cn-shanghai-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest
https://cn-shanghai-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest | oss-cn-shanghai.aliyuncs.com
oss-cn-shanghai.aliyuncs.com | ✅ 已验证 |
| 阿里云 华北2(北京) | https://cn-beijing-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest
https://cn-beijing-alicloud.api.clickzetta.com/api/v1/catalog/iceberg-rest | oss-cn-beijing.aliyuncs.com
oss-cn-beijing.aliyuncs.com | ✅ 已验证 |
更多区域参考云器支持的云平台文档。
ServiceFailureException: Server error: Internal
ServiceFailureException: Server error: Internal
原因:外部格式表(Delta/Hudi/Lance/Paimon)不支持通过 Iceberg REST Catalog 访问,元数据可枚举但数据读取失败。
解决:此类表需通过 Lakehouse Studio 或 JDBC 直接查询,不支持 Spark Iceberg REST 路径。
BadRequestException: Unsupported path: .../metrics
BadRequestException: Unsupported path: .../metrics
原因:Iceberg 客户端在读取完成后尝试上报扫描指标,但 Lakehouse REST Catalog 暂不实现该接口(
/metrics
/metrics
路径)。
影响:仅是日志中的 WARN 级别错误,不影响数据读取正确性,可忽略(阿里云实测确认)。
注意:设置
metrics-reporter-impl
metrics-reporter-impl
为
LoggingMetricsReporter
LoggingMetricsReporter
会添加额外的日志 reporter,但在 Iceberg 1.6.1 中仍会同时触发 REST metrics 上报,WARN 日志依然出现。此 WARN 属于预期行为,无需处理。
认证失败(401)
- 检查用户名和密码是否正确
- 确认 Base64 编码:
base64.b64encode(f"{user}:{pwd}".encode()).decode()
base64.b64encode(f"{user}:{pwd}".encode()).decode()
- 确认
header.Authorization
header.Authorization
值以 Basic
Basic
开头(注意空格)
表不存在(NoSuchNamespaceException
NoSuchNamespaceException
)
- 确认
header.Workspace
header.Workspace
和 default-namespace
default-namespace
拼写正确
- 先用
SHOW TABLES IN cz.public
SHOW TABLES IN cz.public
确认表名
- 检查账号是否有该 workspace 的访问权限