性能对比:查询性能、写入性能、小文件合并策略、Z-Order 排序

性能对比,说白了就是看谁跑得快、谁吃得少。我见过太多团队在选型时只看功能列表,结果上线后查询慢得像蜗牛,写入时又卡死。今天咱们就掰开揉碎,把 Iceberg 和 Delta Lake 在性能上的底裤都扒干净。

核心结论先放这:Iceberg 在查询性能上略占优势,Delta Lake 在写入稳定性上更胜一筹。但具体选谁,得看你的场景。

1. 查询性能:谁让数据跑得更快?

查询性能,我习惯从三个维度看:元数据过滤、数据跳过(Data Skipping)、以及文件格式本身。

1.1 元数据过滤

Iceberg 的元数据层设计得相当精巧。它维护了一个 Manifest 列表,每个 Manifest 文件记录了数据文件的列统计信息(最小值、最大值、空值计数等)。查询时,Iceberg 能快速跳过不相关的 Manifest 和数据文件。

Delta Lake 也有类似机制,但它的元数据存储在事务日志(Transaction Log)中,通过 Checkpoint 文件来加速读取。我个人感觉,Iceberg 的元数据过滤更细粒度,尤其是在多分区、多列的复杂查询中。

维度 Iceberg Delta Lake
元数据存储 独立的 Manifest 文件 事务日志 + Checkpoint
过滤粒度 文件级别,支持列统计 文件级别,支持列统计
分区裁剪 原生支持,效率高 原生支持,效率高
大数据量场景 更优(Manifest 可并行读取) 良好(Checkpoint 可能成为瓶颈)

我的经验:有一次在 PB 级数据上做时间范围查询,Iceberg 的查询计划生成时间比 Delta Lake 快了 30% 左右。原因就是 Iceberg 的 Manifest 列表可以并行扫描,而 Delta Lake 的 Checkpoint 文件在超大集群下会有序列化开销。

1.2 数据跳过(Data Skipping)

数据跳过,说白了就是通过统计信息直接跳过不包含目标数据的文件。Iceberg 和 Delta Lake 都支持,但实现方式略有不同。

  • Iceberg:在 Manifest 中存储了每个数据文件的列统计信息,查询时直接过滤。
  • Delta Lake:在事务日志中记录统计信息,并通过 Checkpoint 文件加速读取。

嗯,这里要注意:Delta Lake 的数据跳过依赖于文件级别的统计信息,如果文件太大,跳过效果会打折扣。Iceberg 则可以通过调整 Manifest 的粒度来控制。

2. 写入性能:谁让数据落盘更稳?

写入性能,我关注的是吞吐量、延迟和并发控制。Delta Lake 在这方面有天然优势,因为它基于 Spark 的 DataFrame API 做了深度优化。

2.1 批量写入

Delta Lake 的批量写入性能非常稳定。它使用了乐观并发控制(Optimistic Concurrency Control),写入时先写事务日志,再写数据文件。冲突检测在提交阶段进行,所以写入吞吐量很高。

Iceberg 的写入性能也不错,但它的提交协议更复杂。Iceberg 使用了一种叫做“快照隔离”的机制,每次写入都会生成一个新的快照。这带来了更好的读一致性,但写入延迟会略高一些。

我曾经踩过的坑:在 Iceberg 上做高频小批量写入(每几秒写几百条数据),结果发现提交冲突率飙升。后来改成了批量攒够一定数据再写入,问题才解决。所以,Iceberg 更适合大吞吐量的批量写入,不适合高频小批次。

2.2 流式写入

流式写入场景下,Delta Lake 的 Auto Optimize 功能非常实用。它能自动合并小文件,减少文件数量。Iceberg 也有类似功能,但需要手动配置。

场景 Iceberg Delta Lake
批量写入(大文件) 优秀 优秀
批量写入(小文件) 一般(需合并) 良好(自动优化)
流式写入 良好(需配置) 优秀(原生支持)
并发写入 良好(乐观锁) 优秀(乐观锁 + 冲突解决)

3. 小文件合并策略:谁让文件更“健康”?

小文件是湖仓一体的大敌。文件太多,NameNode 压力大,查询性能也会下降。Iceberg 和 Delta Lake 都有自己的小文件合并策略。

3.1 Iceberg 的合并策略

Iceberg 提供了 RewriteDataFiles 操作,可以手动或自动合并小文件。它支持多种策略:

  • Binpack:将小文件打包成大文件,简单粗暴。
  • Sort:在合并的同时对数据进行排序,提升查询性能。
  • Z-Order:对多维数据进行排序,优化数据跳过。

我个人习惯用 Binpack 做日常合并,用 Z-Order 做定期优化。

-- Iceberg 小文件合并示例
CALL catalog.system.rewrite_data_files(
  table = 'db.table',
  strategy = 'binpack',
  options = map('target-file-size-bytes', '134217728')
);

3.2 Delta Lake 的合并策略

Delta Lake 的 OPTIMIZE 命令可以合并小文件,同时支持 Z-Order 排序。

-- Delta Lake 小文件合并示例
OPTIMIZE db.table
ZORDER BY (column1, column2);

Delta Lake 的 Auto Optimize 功能可以在写入时自动合并小文件,但会增加写入延迟。我建议在流式写入场景下开启,批量写入场景下手动触发。

我的建议:如果小文件问题严重,可以先做一次全量合并,然后设置定期任务(比如每天凌晨)做增量合并。Iceberg 和 Delta Lake 都支持增量合并,但 Iceberg 的 RewriteDataFiles 更灵活。

4. Z-Order 排序:谁让数据跳过更“聪明”?

Z-Order 排序,说白了就是把多维数据映射到一维空间,让相近的数据物理上挨在一起。这样查询时能跳过更多不相关的数据。

4.1 Iceberg 的 Z-Order

Iceberg 的 Z-Order 排序通过 RewriteDataFiles 操作实现。它支持多列排序,并且可以指定排序的权重。

-- Iceberg Z-Order 排序示例
CALL catalog.system.rewrite_data_files(
  table = 'db.table',
  strategy = 'sort',
  sort_order = 'zorder(column1, column2)'
);

4.2 Delta Lake 的 Z-Order

Delta Lake 的 Z-Order 排序通过 OPTIMIZE 命令实现。它同样支持多列排序,但排序的粒度是文件级别。

-- Delta Lake Z-Order 排序示例
OPTIMIZE db.table
ZORDER BY (column1, column2);
维度 Iceberg Delta Lake
排序粒度 文件内部 + 文件间 文件内部
多列支持 原生支持 原生支持
排序效率 高(可并行) 高(可并行)
查询加速效果 更优(文件间也排序) 良好

注意:Z-Order 排序不是银弹。如果查询条件不包含排序列,Z-Order 反而会带来额外开销。我建议只对高频查询的过滤列做 Z-Order 排序。

5. 知识体系图:性能对比全景

下面这张图总结了本章的核心知识点,方便你快速回顾。

性能对比全景图 性能对比全景图 查询性能 写入性能 小文件合并 Z-Order 排序 核心要点 1. 查询性能:Iceberg 元数据过滤更细粒度,Delta Lake 写入更稳定 2. 写入性能:Delta Lake 乐观并发控制更成熟,Iceberg 适合大吞吐量 3. 小文件合并:Iceberg 策略更灵活,Delta Lake 自动化程度更高 4. Z-Order 排序:Iceberg 支持文件间排序,Delta Lake 支持文件内排序 5. 选型建议:高频查询选 Iceberg,高频写入选 Delta Lake

6. 总结与选型建议

性能对比没有的赢家。我个人的经验是:

  • 查询密集型场景:选 Iceberg。它的元数据过滤和 Z-Order 排序更优,能显著提升查询性能。
  • 写入密集型场景:选 Delta Lake。它的乐观并发控制和自动优化功能更成熟,写入更稳定。
  • 混合场景:如果查询和写入都很重要,可以考虑两者结合。比如用 Iceberg 做查询层,用 Delta Lake 做写入层。

嗯,最后提醒一句:性能测试一定要基于你的实际数据和查询模式。别盲目相信 Benchmark,自己跑一遍最靠谱。