1926 字
10 分钟
在 kind 上自建 Lakehouse(三):Iceberg 读性能横评 —— ClickHouse vs Doris vs StarRocks(TPC-H SF10)

这是「自建 Lakehouse 实战」系列第三篇,也是最硬核的一篇。前两篇(搭建互操作)解决了”能不能用、能不能互通”;本篇回答”谁读得快”。 对象:ClickHouse 26.5、Doris 4.1.1、StarRocks 4.1.1,都读同一张 Lakekeeper/GCS 上的 Iceberg 表。(Trino 和 Spark 在这里是数据写入器,用来对照”写入器是否影响读性能”。)

一、基准设置#

  • 数据集 SF10。用 Trino 的 tpch connector CTAS 写成 Iceberg bench_trino.*(lineitem 5999 万行 = 1.53GB / 84 个月度分区;orders 1500 万、customer 150 万、part 200 万…),再用 Spark CTAS 复制成 bench_spark.*(同内容、不同写入器)。实测 Trino 与 Spark 的文件布局几乎一致(都约每分区 1 文件)。
  • join 测试:每个引擎各自加载一份本地原生表(lineitem/orders/customer/nation/region),以测”本地表 + Iceberg 表”混合 join。
  • 冷读 vs 热读:冷读 = 关闭引擎数据缓存跑 1 次(纯 GCS 扫描);热读 = 开缓存、预热后取 3 次平均。
  • 拓扑(重要前提) = 2 BE;StarRocks 后期用官方 operator 部成 1 FE + 2 BE;ClickHouse 的 DataLakeCatalog + 本地表为单节点。所以下面的对比并非严格同拓扑,解读时请带上这个上下文。

查询场景:S1 单 Iceberg 表(全表扫描 / 分区裁剪 / TPC-H Q1 聚合);S2 Iceberg 表之间 2→3→4 路 join;S3/S4 本地表 + Iceberg 表混合 join(small/big 指结果集大小)。

二、总览:热读耗时#

各引擎热读耗时对比,对数轴

热读耗时(Trino 写入数据,对数轴)。越低越好。

场景ClickHouseDorisStarRocks
S1 全表扫描5.04.10.9
S1 分区裁剪1.00.90.5
S1 group by(Q1)6.55.21.8
S2 2 表 join10.46.32.0
S2 3 表 join17.39.64.7
S2 4 表 join17.010.04.7
S3 小(3本地+1iceberg)2.11.10.6
S3 大3.63.01.3
S4 小(3本地+3iceberg)93.01.91.1
S4 大96.53.82.7

热读状态下 StarRocks 一骑绝尘(简单查询亚秒级),Doris 居中,ClickHouse 在简单扫描上接近、但一到宽 join(S4)就崩到 90 秒级——这点单独说。

三、join 随表数扩展 的单节点断崖#

多表 join 冷读随表数扩展

Iceberg 多表 join 冷读随表数扩展(2→3→4 表)。

2/3/4 表 join 时,Doris、StarRocks 基本平稳在 10 秒级以内,而 ClickHouse 从 3 表的 18 秒陡增到 4 表的 38 秒。更极端的是 S4(6 张表的混合 join):

  • ClickHouse 热读 90–100 秒,而 Doris/StarRocks 只要 2–6 秒。
  • 而且即便给 ClickHouse 喂 20GB 缓存,S4 仍停在 ~80 秒——这是计算瓶颈,缓存救不了
  • 连 S4-small(结果集很小)也要 90 秒,因为 6000 万行的本地扫描 + 多路 hash build 主导了耗时,与结果大小无关。

根因是 ClickHouse 这套是单节点跑宽多路 join,缺少分布式 shuffle 的并行优势。它真正有竞争力的场景是简单扫描和分区裁剪。

四、写入器影响 写 vs Spark 写#

Spark 写与 Trino 写的冷读耗时比值

写入器影响/Trino 冷读耗时比值,>1 表示读 Spark 写的数据更慢。

这是个常被问到的问题:同样的数据,用 Trino 写还是 Spark 写,会不会影响下游读性能?结论:

  • Doris、StarRocks:基本没差别(在噪声范围内,有时 Spark 写的还更快)。
  • ClickHouse:读 Spark 写的数据冷读稳定慢 20–30%(S1 5.3→6.7、S2 join2 12.3→16.0、join3 18.2→22.6);热读差距变小。

由于两者文件布局几乎一致,ClickHouse 这点残留的冷读惩罚,推测来自 parquet row-group/统计/编码的细微差异与 CH 的 GCS reader 的交互。净结论:选 Trino 还是 Spark 当写入器,对读性能影响不大,只有 ClickHouse 冷读对 Spark 文件有轻微惩罚。

五、缓存深挖 的快是不是全靠缓存?#

StarRocks 不同缓存配置对比 Doris

StarRocks 全量缓存 vs ~1GB vs 关闭,对比 Doris(对数轴)。

StarRocks 热读那么快,很大程度来自它的 datacache。于是专门压了三档:全量缓存、缩到 ~1GB、完全关闭。发现:

  • 当缓存小于工作集,StarRocks 的优势会崩塌:全量缓存下 1–2 秒的查询,在 1GB 缓存下变成 5–10 秒(≈ 裸 GCS 扫描 ≈ Doris);S2 join2 慢了 5 倍(2.0→10.1 秒),此时反而输给 Doris(6.3 秒)。
  • Doris 几乎不吃缓存:热读 ≈ 关缓存(如 join4 是 9.96 vs 9.14 秒)——它在这些聚合上是计算瓶颈。
  • StarRocks 完全关缓存、直连 GCS 的路径在本环境不稳定/groupby 会报错(连接超时、parquet streaming reader 的 “two dictionary page” / “io ranges overlapped”),只有简单扫描能跑。

所以一句话:StarRocks 是”缓存命中”的赌注——热集装得下缓存时碾压全场,装不下就跌回 GCS 速度;Doris 则是可预测、与缓存无关

六、ClickHouse 缓存修正:并不是不缓存,而是默认没配#

一开始 ClickHouse 看起来”又慢又不缓存”,后来发现这是配置缺口:DataLakeCatalog 默认没有文件系统缓存。补上具名缓存(filesystem_caches 配 path + max_size,查询时 SET enable_filesystem_cache=1)后,ClickHouse 会缓存对象存储数据,热读快 2–4.5 倍(S1 扫描 5.5→1.2 秒、S2 join2 12.5→2.8 秒)。给足 20GB 缓存后,它在简单查询上甚至反超 Doris(仅次于 StarRocks)。 三引擎都给足缓存的公平对比

公平对比:三引擎都给足缓存(ClickHouse 20GB / Doris 热 / StarRocks 全量,对数轴)。

这张”都给足缓存”的公平对比里能清楚看到:简单扫描/聚合 ClickHouse 与 Doris 接近、StarRocks 最快;但 S4 宽 join,ClickHouse 仍是 80 秒的断崖——再次印证那是单节点计算瓶颈,缓存无能为力。

七、StarRocks 拓扑:单 BE vs 1 FE + 2 BE#

StarRocks 单BE与1FE2BE对比 Doris

StarRocks allin1(1 BE)vs 1 FE + 2 BE vs Doris(2 BE),热读对数轴。缺失的浅红柱 = allin1 在大 join 上报错。

为了和 Doris 同口径(都 2 BE),又用官方 operator 把 StarRocks 重部成 1 FE + 2 BE,有两个发现:

  • 之前那个 invalid pos 大 join 报错是”单 BE 特有”的:在 1 FE + 2 BE 上,S3-big / S4-big 干净跑过(1.3 秒 / 2.7 秒热读)。
  • 对比自己的 allin1(1 BE),2 BE 在简单/中等查询上反而略慢(S1 group by 1.8 vs 0.8、S2 4 表 join 4.7 vs 3.4):在单机 ~6000 万行这个量级,跨 BE 的 exchange/shuffle + 拆分的 datacache 开销,比一个”胖 BE”省下的更多。

即便如此,1 FE + 2 BE 的 StarRocks 仍在各场景全面快于 Doris(2 BE)。

八、选型小结#

  • StarRocks:热读最快,简单查询亚秒级——前提是热数据装得进缓存;否则回落到 GCS 速度。关缓存直连对象存储在本环境还不稳。适合”热集明确、缓存喂得饱”的交互式分析。
  • Doris:最稳、最可预测,几乎不依赖缓存(计算瓶颈型),宽 join 表现扎实、无失败。适合”工作集大、对延迟稳定性要求高”的场景。
  • ClickHouse:简单扫描/分区裁剪有竞争力(配好文件系统缓存后更佳),但单节点跑宽多路 join 会断崖(S4 达 80–100 秒),且缓存救不了。它的强项不在这种多表 join 工作负载。
  • 写入器(Trino vs Spark):对读性能影响可忽略,仅 ClickHouse 冷读对 Spark 文件有约 20–30% 的轻微惩罚。

所有数字都是在同一张 Lakekeeper/GCS Iceberg 表上跑出来的——这正是开放表格式的价值:一份数据,多引擎按需取用,各取所长。


📚 本文是「自建 Lakehouse 实战」系列(共 6 篇):

  1. 搭建篇 —— Lakekeeper + GCS + 五大引擎接入
  2. 互操作篇 —— 跨引擎读写、MERGE 与 positional delete 合规性
  3. 性能篇(本文) —— Iceberg 读性能横评 vs Doris vs StarRocks
  4. 联邦篇 —— Trino 联邦查询 vs 专用 Doris:冷数据 Iceberg ⋈ 关系表的代价
  5. 扩容篇 —— Doris 存算分离 BE 扩容量化:加算力到底快多少
  6. 实时入湖篇 —— Flink CDC / PostgreSQL → Doris 存算分离,延迟与节点数的真相
在 kind 上自建 Lakehouse(三):Iceberg 读性能横评 —— ClickHouse vs Doris vs StarRocks(TPC-H SF10)
https://notes.ezworker.cc/posts/lakehouse-on-kind-3-benchmark/
作者
jayzhu
发布于
2026-06-07
许可协议
CC BY-NC-SA 4.0