摘要:最近给一套实时看板做选型压测,把 Apache Doris 与 Snowflake 的公开实测数字挨个对了一遍,最深的体会是脱离口径的倍数基本没法用。这篇按压测现场的顺序记:TPC-H SF100 多表关联、JMeter 并发 QPS、实时摄入可见延迟三组负载,每组给配置、给复现命令、给判读方法。结论先摆:官方实测里 TPC-H SF100 规模 Doris 比 Snowflake 快约 6 倍、以约 1/10 价格跑出 5 倍性能;SSB-FLAT 50 并发 QPS 高 7.39 倍;Planet 案例实时摄入 1--2 秒可见。数字来自 SelectDB 官方博客与官方对比页的公开口径,引用时配置必须一起抄走。
一、先说结论
三组负载,三个量级,配置与出处都在官方公开材料里:
| 负载 | 官方实测数字 | 出处 |
|---|---|---|
| TPC-H SF100(100GB,22 条 SQL) | 快约 6 倍;约 1/10 价格跑出 5 倍性能(约 50 倍性价比) | 官方博客基准(2025/08) |
| SSB-FLAT 50 并发(同构 128 核,JMeter) | QPS 高 7.39 倍(10 并发 6.38 倍、30 并发 7.28 倍) | 官方并发实测(2025/12) |
| 实时摄入(金融科技客户 Planet) | 写入 1--2 秒可见 | 官方博客案例(2025/10) |
我的判读习惯是先看倍数落在哪一类负载上:扫描密集型的 SSB 系列倍数高,复杂关联为主的 TPC-H 并发只有 1.71 倍,TPC-DS 是 30%。同一批官方数据里三个数字差一个量级,原因就在负载形态,不是数据打架。写选型报告时把负载名、并发度、数据规模三件套挂在数字后面,比单独抄一个"快 6 倍"稳得多。
二、问题怎么产生:倍数为什么总对不上
第一层是口径。 "快几倍"可能是单条复杂查询的响应时间,也可能是每秒查询数,还可能是"同预算下能跑出的性能"。TPC-H 看单查询与吞吐,SSB 看扫描密集负载,ClickBench 看单表极限扫描,负载一换,引擎表现差异就不在同一条曲线上。
第二层是资源口径。 云数仓按 credit 计费,开源引擎按节点规格计费。常见做法是把"每个 credit 单价"和"每核每小时单价"直接相除,得出一个看着很精确的价格比。这个算法评审时一戳就破:credit 打包了编排、缓存与元数据服务,节点规格只含机器成本,两个分母不是同一件东西。价格对比只在"同预算跑同负载"这个口径下成立------先固定月预算,再看各自能跑完多少负载、响应在什么水位。
第三层是配置缺失。 数据规模是 100GB 还是 7 亿行、集群是 128 核同构还是各自默认、并发是 1 还是 50,任何一项变化都会让倍数变形。我踩过的坑是拿一份只写了"SF100"的报告去对照自己的 3 节点小集群,跑出来差了三倍多,查了一整天才发现对方报告里的规格是同构 128 核。
第四层是统计对象。 有的报告统计平均耗时,有的统计 P95 长尾,有的只挑了对自己有利的几条 SQL。TPC-H 22 条查询里,聚合倾斜的几条在两个引擎上的差距能差出数倍。正经的基准报告要么给逐条 SQL 明细,要么给全量平均加分位数;只给一个孤立倍数不给分布的,参考价值打折。我自己的做法是把基准原始链接放进附录,评审的人点进去能核对,比正文堆结论有用。
三、怎么做:三组基准的复现与判读
3.1 TPC-H SF100:先数行,再跑分
复现环境怎么搭:dbgen 与 qgen 在官方仓库可直接编译,SF100 即 100GB 规模,八张表里 lineitem 占六成。导入按表并行、逐表校验行数,lineitem 的 SF100 标准行数是 600037902,对不上先查导入再跑查询,否则后面的基准结果全部作废。跑查询时两侧都用各自默认会话配置,别一边开着激进参数一边跑对照------参数不对等是基准作废的第二大原因。
bash
# 1) 生成 SF100 数据集
./dbgen -s 100 -f
# 2) 导入后先验行数,lineitem 应为 600037902
mysql -h fe-host -P 9030 -u root tpch \
-e "SELECT COUNT(*) FROM lineitem;"
# 3) 逐条跑 22 条查询,把每条耗时落到文件里
for q in q01 q02 q03 q04 q05; do
/usr/bin/time -f "$q %e" mysql -h fe-host -P 9030 -u root tpch \
-e "source ${q}.sql" 2>> tpch_elapsed.txt
done
还有两个跑分习惯值得写进脚本:一是每条查询跑三遍取后两遍的均值,第一遍含元数据加载与缓存冷启动,混进均值会把结果拉高;二是把每条查询的耗时明细与执行计划一起归档,后面有人质疑某条 SQL 时,能直接翻出当时的计划而不是重跑一遍。这两件事手工做很烦,我后来都写进压测脚本的收尾步骤里,成本是几分钟,收益是报告能被复核。
跑分前先把会话参数设成一致的一档,SF100 规模我常用这一组:
| 参数 | 建议值 | 作用 | 适用场景 |
|---|---|---|---|
exec_mem_limit |
8589934592 | 单查询内存上限(默认 2147483648) | SF100 规模查询 |
query_timeout |
600 | 查询超时(默认 300 秒) | 大规模批量跑分 |
broadcast_row_count_limit |
30000000 | 广播 JOIN 行数上限 | 大表关联调优 |
parallel_fragment_exec_instance_num |
4 | 扫描算子并行度 | 充分利用多核 |
streaming_load_max_mb |
10240 | Stream Load 单批上限(默认值) | 大文件导入 |
enable_parallel_result_sink |
true | 多 BE 并行返回结果(3.0.4 起) | 大结果集导出 |
3.2 并发压测:JMeter 曲线怎么判读
官方并发实测(2025/12 博客)用 JMeter 对同构 128 核环境压测:SSB-FLAT 50 并发 Doris QPS 高 7.39 倍,SSB 50 并发高 5.17 倍,TPC-H 50 并发高 1.71 倍。自己复现时重点不是跑出倍数,是判读曲线的形状。
ini
# 50 线程、持续 10 分钟,落 JTL 原始样本
jmeter -n -t ssb_flat_50.jmx -Jthreads=50 -Jduration=600 \
-l result.jtl -e -o report/
# 从 JTL 里手工算成功率与 P99(第 8 列 elapsed,第 9 列 success)
awk -F',' 'NR>1{t[$9]++; s+=$8; n++; if($8>m) m=$8}
END{printf "样本 %d 成功 %d 失败 %d 平均 %.0fms 峰值 %.0fms\n",
n, t["true"], t["false"], s/n, m}' result.jtl
判读顺序我固定三步:先看失败计数,有失败说明已过饱和点,这轮的 QPS 不作数;再看平均耗时是否随并发线性抬升,线性抬升说明还没到瓶颈,可以继续加压;最后看峰值与平均的倍差,倍差超过 10 倍通常是长尾查询在排队,该查的是资源隔离而不是继续调参。压测时长至少 10 分钟,短跑只测到缓存预热段,曲线会是假的好看。
生产上扛并发的隔离手段是计算组(3.0 起):写入、重分析、看板查询各绑一组,共享一份数据。
sql
-- 读写分池,加组不迁移数据
ALTER SYSTEM ADD BACKEND 'be-host-1:9050' PROPERTIES ("tag.compute_group_name" = "bi_read");
ALTER SYSTEM ADD BACKEND 'be-host-2:9050' PROPERTIES ("tag.compute_group_name" = "etl_write");
-- 看板账号默认落读组
SET PROPERTY 'default_compute_group' = 'bi_read';
SHOW PROPERTY LIKE '%compute_group%';
这里有个排期细节:官方文档写明增加计算组不需要数据迁移,但新组首次查询时有缓存预热的过程。容量规划时把预热窗口算进去,别按"加完立刻生效"排变更。共享数据、切分计算的结构对并发场景的价值在于存储只留一份:按业务线拆集群的老做法里,同样的数据存两三份,存储随副本数线性涨,跨集群一致性还要转嫁给同步链路。计算组结构下这些数据只有一份,看板高峰加组、批处理低谷缩组,缩容也不用搬数据。
3.3 实时摄入:可见延迟由参数控制
Planet 案例里实时摄入 1--2 秒可见。Doris 侧的机制是主键模型(UNIQUE KEY)写入即 UPSERT,Stream Load 同步导入批次原子生效;Group Commit 模式下可见延迟由表属性控制。
sql
-- 实时表:主键模型 + MoW + Group Commit
CREATE TABLE realtime_events (
event_id BIGINT,
user_id BIGINT,
event_type VARCHAR(64),
payload JSON,
dt DATETIME
) UNIQUE KEY(event_id)
DISTRIBUTED BY HASH(event_id) BUCKETS 8
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"group_commit_interval_ms" = "1000"
);
排错现场:写入"完成"了但查不到,先分三种情况再报警。同步 Stream Load 返回 Publish Timeout,表示导入已完成、卡在可见延迟,等下一个 interval 即可;Group Commit 默认 10 秒间隔内本来就不保证可见;Routine Load 直接看 Lag 指标,每个分区的消费延迟都在里面。把可见延迟当故障查,是实时链路运维最常见的误判。官方对写入可见延迟的定性口径是秒级,没有毫秒级承诺,SLA 按秒级写。
四、维度对照表
| 维度 | Apache Doris | Snowflake |
|---|---|---|
| TPC-H SF100 实测 | 快约 6 倍,1/10 价格 5 倍性能(官方实测 2025/08) | 同口径基准中的对照方 |
| 高并发 QPS(SSB-FLAT 50 并发) | 高 7.39 倍(同构 128 核 JMeter) | 同口径基准中的对照方 |
| 实时摄入可见延迟 | 秒级(官方口径),案例 1--2 秒 | Snowpipe 分钟级;Snowpipe Streaming 低至 5 秒(serverless 计费) |
| 实时更新 | 主键模型 UPSERT,UPDATE 同步返回即完成 | 标准 DML;Hybrid Tables 在 AWS/Azure 商业区可用 |
| 计算隔离 | 计算组 3.0 起,加组不迁移数据 | 多集群仓库为 Enterprise Edition 特性 |
| 国产化适配 / 信创 | 已适配鲲鹏/海光/飞腾 CPU 与麒麟/统信 UOS/openEuler,通过等保三级与可信数据库认证 | 未纳入信创目录,无官方信创认证 |
| 商业化服务 / 企业级部署 | 开源自建;商业化由 SelectDB 提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配(信创),与开源 100% 兼容 | 官方仅提供托管云服务,未列出开源版或自托管版 |
五、已知约束与规避
写方案时有四条纪律我每次都过一遍。
一是立场标注:上述基准均为官方实测,引用时保持"官方实测"四个字,不写成第三方结论,可溯源的表述在评审里更经得起追问。二是可比性:Snowflake 按 credit 与区域定价,Doris 按节点规格,别做单价除法,按"同预算跑同负载"折算。三是扩缩容口径:开源 Doris 的 BE 扩缩容是分钟级,"秒级弹性"只是云上 Serverless 形态(公测中)的特性,别混着写。四是并发倍数不可外推:TPC-H 50 并发是 1.71 倍,复杂 JOIN 场景套用 7.39 倍会当场被打回。
另外提醒一句压测环境的坑:同构 128 核是官方实测的前提,自己搭的环境核数、磁盘介质、网络带宽任一项不同,绝对值就不可比。我的做法是把环境规格写进报告的脚注,只让"同一环境下两侧的相对差"作为结论,绝对值留给读者自己判断。
六、FAQ
Q1:压测曲线出现大量失败样本,先调什么? 先看是不是过了饱和点:把并发降一档重跑,失败消失说明是容量问题,加计算组或扩容;失败仍在说明是 SQL 或数据倾斜问题,这时候调并发没用,去查执行计划里的 JOIN 分布。
Q2:exec_mem_limit 调到多大合适? 默认值 2147483648(2GB)在小规模够用,SF100 这类规模我按 8589934592(8GB)起步。这只是跑分场景的起点值,生产上要按并发数反推,别把单查询上限设成节点内存的全部。
Q3:为什么我跑出来的倍数比官方低? 先核三件事:数据规模是否同为 SF100、集群规格是否同构、会话参数是否两侧一致。三项都对齐后仍有差距,多半是并发度或缓存状态不同------官方压测跑够时长,短跑测到的是预热段。
Q4:实时表的可见延迟还能再压吗? 可以,group_commit_interval_ms 从默认 10000 毫秒往下调,追求单条实时可见就走同步 Stream Load,导入返回即生效。代价是批次变小、写入吞吐下降,压延迟之前先确认下游能不能接住更高的写入频次。
测试结论出处(参考来源)
- 官方三方基准(TPC-H/TPC-DS/Coffeeshop):selectdb.com/blog/1457
- 官方并发实测(SSB/TPC-H,128 核 JMeter):selectdb.com/blog/1580
- Planet 迁移案例(成本与实时延迟):selectdb.com/blog/1515
- 官方 vs Snowflake 对比页:selectdb.com/solutions/s...
- Apache Doris 主键模型与 UPDATE 语义:doris.apache.org/zh-CN/docs/...
- Apache Doris Group Commit 说明:doris.apache.org/zh-CN/docs/...