老梁聊IT头像
实时数仓选型:Flink+StarRocks vs ClickHouse vs Doris 深度对比与决策树封面图

实时数仓选型:Flink+StarRocks vs ClickHouse vs Doris 深度对比与决策树

一、先定义清楚你要解决什么

"实时数仓"是个被用滥的词。选型之前,必须先分清你要支撑的是哪一种负载,因为这三种形态的最优解完全不同:

形态 A:追加型日志分析(Append-only)

特征:数据只增不改,典型如用户行为日志、IoT 遥测、埋点事件。查询多为"时间范围 + 维度下钻 + 聚合"。

典型查询:
SELECT dt, channel, COUNT(DISTINCT user_id), SUM(amount)
FROM user_event_log
WHERE dt BETWEEN '2026-09-01' AND '2026-09-23'
  AND event_type = 'purchase'
GROUP BY dt, channel;

核心诉求:单表扫描快、压缩比高、写入吞吐大。 → ClickHouse 的主场。

形态 B:可变状态宽表(Mutable / UPSERT)

特征:数据会更新,典型如订单状态(待付款→已付款→已发货)、用户画像标签、库存余额。需要按主键做 UPSERT,且查询要求读到最新状态。

典型查询(订单状态实时看板):
SELECT status, COUNT(*), SUM(amount)
FROM order_wide
WHERE create_time >= '2026-09-23'
GROUP BY status;   -- 要求 status 是最新状态,不是历史快照

核心诉求:主键更新(UPSERT)、去重、多表 Join 打宽。 → StarRocks / Doris 的主场。

形态 C:高并发点查(Serving)

特征:QPS 数千到数万,查询条件是主键或索引,返回单条或少量记录。典型如"用户详情页实时指标""风控特征查询"。

典型查询:
SELECT * FROM user_profile_agg WHERE user_id = 10086;
-- QPS 峰值 8000,要求 p99 < 20ms

核心诉求:点查延迟、并发能力、缓存。 → 三者都能做,但都不是最优解(真要极致点查该上 HBase/Redis + 宽表),不过在"顺便支撑"层面 StarRocks 的行存 + 索引更合适。

重要提醒:很多团队选型的失败,源于用形态 A 的引擎去扛形态 B 的负载(最常见:用 ClickHouse 存订单状态表,然后被 UPSERT 折磨到崩溃)。先分类,再选型。

二、选型维度定义:别只比跑分

我见过太多"ClickHouse vs Doris 谁快"的对比,最后发现赢的那个在真实业务里跑不起来。原因是维度定义错了。生产选型至少要评估这 8 项:

维度为什么重要权重建议
1. 写入吞吐与实时性能不能吃下 Flink 的写入峰值★★★★★
2. 单表查询性能形态 A 的核心★★★★★
3. 多表 Join 能力形态 B 的核心,也是分水岭★★★★★
4. 数据更新(UPSERT)状态类数据的刚需★★★★☆
5. 高并发点查形态 C★★★☆☆
6. 运维复杂度决定你能不能养得起★★★★☆
7. 生态集成Flink/Kafka/Hive/Iceberg 对接★★★★☆
8. 成本机器 + 人力★★★★☆

三、三引擎架构对比

┌──────────────────────────────────────────────────────────────┐
│ ClickHouse:列存 + MergeTree + 向量化                          │
│ ┌────────┐  ┌────────┐  ┌────────┐                           │
│ │ Shard1 │  │ Shard2 │  │ Shard3 │   ← 依赖 ZK/Keeper 管副本 │
│ │(Repl)  │  │(Repl)  │  │(Repl)  │                           │
│ └────────┘  └────────┘  └────────┘                           │
│ 特点:稀疏索引 + 数据分区内有序,单表扫描极快                    │
│ 软肋:分布式 Join 能力弱(需改写/预聚合),UPSERT 靠            │
│       ReplacingMergeTree 异步合并,读到"最新"要加 FINAL        │
├──────────────────────────────────────────────────────────────┤
│ StarRocks:BE + FE 架构,全面向量化 + CBO 优化器               │
│ ┌─────────────┐                                              │
│ │ FE (元数据/CBO)│ ← 支持复杂查询计划、Join 重排序             │
│ └──────┬──────┘                                              │
│  ┌─────┴─────┬─────────┬─────────┐                           │
│  │ BE1       │ BE2     │ BE3     │  ← 列存 + 主键表支持       │
│  └───────────┴─────────┴─────────┘    实时 UPSERT            │
│ 特点:CBO 优化器强,多表 Join / 大表关联表现好                  │
│       Primary Key 表原生支持 UPSERT,查询即最新                 │
├──────────────────────────────────────────────────────────────┤
│ Doris:FE + BE 架构(与 StarRocks 同源,后分道)               │
│ ┌─────────────┐                                              │
│ │ FE (MySQL协议)│ ← 兼容 MySQL 协议,上手最顺                  │
│ └──────┬──────┘                                              │
│  ┌─────┴─────┬─────────┬─────────┐                           │
│  │ BE1       │ BE2     │ BE3     │  ← Unique/Aggregate 模型   │
│  └───────────┴─────────┴─────────┘                           │
│ 特点:三种数据模型(Duplicate/Unique/Aggregate)               │
│       MySQL 协议亲和,生态集成(Flink CDC)成熟                 │
└──────────────────────────────────────────────────────────────┘

三者的血缘关系值得一提:StarRocks 和 Doris 同源(都源自 Apache Doris 早期版本),所以架构理念、数据模型、甚至很多 SQL 语法都相近。差异主要在:StarRocks 在向量化执行器和 CBO 优化器上做了更彻底的重构(查询性能特别是 Join 上更强),Doris 则在社区生态和 MySQL 协议兼容性上持续投入。

四、场景化压测:四类基准实测

以下数据来自我们内部的基准环境(3 台 32C128G NVMe,各自部署默认配置),用于横向对比量级,不代表官方最优。数据集:SSB 1000 万行 + 自建订单状态表 2000 万行。

基准 1:单表明细聚合(形态 A)

SELECT dt, channel, COUNT(*), SUM(amount)
FROM event_log WHERE dt >= '2026-09-01' AND dt <= '2026-09-23'
GROUP BY dt, channel ORDER BY dt;
引擎耗时相对说明
ClickHouse0.42 s1.0×列存 + 向量化,单表扫描最强
StarRocks0.68 s1.6×接近,略逊
Doris0.81 s1.9×同量级

结论:单表扫描 ClickHouse 领先,但三者在同一量级(都在 1 秒内),这点差距通常不构成选型依据。

基准 2:多表 Join(形态 B 的分水岭)

-- 事实表 2000万 + 维表1(50万)+ 维表2(10万)
SELECT c.region, p.category, SUM(o.amount)
FROM orders o
JOIN customers c ON o.cust_id = c.id
JOIN products  p ON o.prod_id = p.id
WHERE o.dt >= '2026-09-01'
GROUP BY c.region, p.category;
引擎耗时相对说明
StarRocks1.24 s1.0×CBO 自动选 Colocate/Bucket Shuffle Join
Doris1.87 s1.5×优化器持续改进,同量级
ClickHouse4.6 s3.7×需手动改写为 IN/字典,原生分布式 Join 弱

这是真正的分水岭。ClickHouse 的分布式 Join 是出了名的坑,大表 Join 容易 OOM,通常需要:

  • 把维表做成 Dictionary(内存字典);

  • 或者改写为子查询 + IN;

  • 或者在 Flink 侧就打宽(预 Join 成大宽表)。

前两种方案在小维表上可行,一旦维表变大或需要多跳 Join,维护成本急剧上升。

基准 3:UPSERT 实时更新(形态 B 的刚需)

场景:Flink CDC 实时同步订单表,状态字段持续更新,要求查询即最新。

引擎实现方式更新吞吐查询一致性坑
StarRocksPrimary Key 表~8 万行/s强一致(查询即最新)主键表内存占用高
DorisUnique Key(MoW)~6 万行/s强一致Merge-on-Write 有写放大
ClickHouseReplacingMergeTree~15 万行/s最终一致需 FINAL 或 argMax,异步合并

ClickHouse 的写入吞吐反而最高,但它给的是最终一致,新写入的数据在未合并前,查询可能返回旧版本(或重复行)。要读到最新必须加 FINAL(性能损失明显)或用 argMax 聚合改写。而 StarRocks/Doris 的主键/唯一键模型,查询天然就是最新的。

这一项往往是一票否决项。如果你的业务有"订单状态实时看板""库存实时余额",用 ClickHouse 会非常痛苦。

基准 4:高并发点查(形态 C)

单主键查询,SELECT * FROM user_agg WHERE user_id = ?,逐步加压:

引擎QPS(p99 < 50ms)p99 延迟说明
StarRocks~1200018 ms行存 + 主键索引,点查优化到位
Doris~900026 ms支持行存/点查优化
ClickHouse~550041 ms列存点查需读多列,非其强项

注意:这三者在形态 C 上都不是专业选手。如果 QPS 要到数万且延迟要求个位数毫秒,正确做法是 HBase/Redis + OLAP 分层,或至少加一层缓存。别硬扛。

五、逐个剖析与场景推荐

StarRocks:极速统一,多表关联与湖仓一体首选

优势:

  • CBO 优化器强,多表 Join / 复杂查询表现最好;

  • Primary Key 表原生 UPSERT,实时更新强一致;

  • 湖仓一体(查 Iceberg/Hive/Hudi 外表)能力成熟,"一份数据多处分析";

  • 支持物化视图自动改写,对 BI 场景友好。

代价:

  • 主键表(Primary Key)内存占用较高(主键索引常驻内存),数据量大时内存成本高;

  • 生态相对年轻,某些周边工具不如 ClickHouse 丰富;

  • 运维需要对 FE/BE 有理解,扩容缩容有门槛。

适合:

  • 需要多表 Join 的实时报表 / Ad-hoc 分析;

  • 订单状态、用户画像等需要 UPSERT 的场景;

  • 想做湖仓一体、减少数据搬运的团队;

  • 已经有 Flink 做实时打宽、需要下游强一致查询的链路。

ClickHouse:单表之王,成本与极限性能首选

优势:

  • 单表扫描与压缩比一骑绝尘(压缩后体积常为原始 1/5~1/10,省存储);

  • 写入吞吐极高(批量写入百万行/秒级);

  • 生态极其丰富(Grafana 集成、各种表引擎、周边工具);

  • 运维简单,单机就能发挥很强性能,小规模团队友好。

代价:

  • 分布式 Join 弱(最大软肋);

  • UPSERT 是最终一致,状态类数据要绕;

  • 并发能力一般(默认配置下单机并发不宜过高,需要调优);

  • 集群规模大时,ZK/Keeper 和副本管理会变复杂。

适合:

  • 追加型日志 / 埋点 / 时序数据分析(它的绝对主场);

  • 单表为主的宽表查询(在 Flink 侧打宽后写入);

  • 对存储成本敏感、数据量极大的场景(压缩比是硬优势);

  • 团队规模小、希望运维简单的场景。

Doris:生态亲和,上手最快

优势:

  • MySQL 协议兼容,任何 MySQL 客户端/BI 工具直连,学习成本最低;

  • 三种数据模型(Duplicate/Unique/Aggregate)覆盖常见需求;

  • 生态集成成熟(Flink-Doris-Connector、Spark-Doris、CDC 整库同步);

  • 社区活跃,中文资料丰富。

代价:

  • 极限性能(特别是复杂 Join)略逊 StarRocks;

  • 部分高级特性(如极强的 CBO)迭代中;

  • 与 StarRocks 功能重叠,选型时容易纠结。

适合:

  • 团队已有 MySQL 技术栈、希望平滑迁移;

  • 需要快速上线、不愿投入大量学习成本;

  • 以固定报表为主的场景;

  • 需要整库同步(CDC)能力成熟的场景。

六、决策树:可以直接照抄

补充:混合部署是常态,不是妥协

现实中最常见的架构其实是分层混合:

Flink 实时链路
     │
     ├──> 【ClickHouse】存原始明细日志(形态 A)
     │        └─ 支撑明细查询、日志检索、用户行为分析
     │
     └──> 【StarRocks】存打宽后的状态宽表(形态 B)
              └─ 支撑实时看板、多维报表、UPSERT 场景
                    │
                    └─> 【Redis】缓存热点指标(形态 C)

理由很简单:让每个引擎做自己最擅长的事。ClickHouse 存海量明细(省存储、写入快),StarRocks 存需要 Join 和更新的宽表,Redis 扛点查。维护两套引擎的额外成本,远小于"用一个引擎硬扛所有形态"带来的痛苦。

我们的实践:日志类数据(日均 80 亿条)走 ClickHouse,订单/用户状态(2000 万行)走 StarRocks,热点聚合结果缓存到 Redis。整体机器成本比"全用 StarRocks"低约 35%,比"全用 ClickHouse"少了无数 UPSERT 的绕路。

6.4 迁移路径:从 ClickHouse 迁到 StarRocks 怎么做到不停机

很多团队是"先用 ClickHouse 起步,后来被 Join 和 UPSERT 逼着迁移"。直接停机切换不现实,我们用的是双写 + 灰度切读四步法:

第一步:双写(2 周)。Flink Sink 同时写 ClickHouse 和 StarRocks,两边都写全量。这一步的风险是写入压力翻倍,需要先评估 Flink 作业的资源余量(通常 CPU 加 30% 够用)。

第二步:数据一致性比对(1 周)。写个比对任务,每天抽样比对两边的核心指标:

-- 两边各跑一次,比对结果差异
SELECT dt, COUNT(*) AS cnt, SUM(amount) AS amt
FROM order_wide WHERE dt = '2026-09-23' GROUP BY dt;

差异率降到 0.01% 以下再进入下一步。这一步千万别跳,我们第一次迁移时跳过比对,结果 StarRocks 侧有个时区配置问题导致跨天数据错位,上线后才被发现。

第三步:灰度切读(2 周)。按业务模块逐步把查询切到 StarRocks:先切内部看板(出问题影响小),再切核心报表,最后切对外 API。每个阶段观察 2~3 天,重点看查询耗时和错误率。

第四步:下线 ClickHouse 写链路。确认所有读流量都切走后,停掉 ClickHouse 的 Sink,保留 ClickHouse 集群只读一段时间(作为回滚备份),一个月后彻底下线。

整个周期大约 5~6 周,其中第二步的比对是最容易被压缩但最不该压缩的环节。另外提醒:三步走期间两套引擎并存,机器成本会翻倍,要提前跟老板说清楚这是临时状态。

6.5 容量规划:集群到底要几台机器

选型定了,马上要回答一个很现实的问题:集群规模怎么估?给一套能直接套的公式,避免"拍脑袋订机器"上线后要么不够用要么浪费。

ClickHouse 侧:单节点(32C128G NVMe)单表扫描可支撑约 5~10 亿行明细的亚秒查询,写入吞吐百万行/秒级。节点数按"原始数据总量 ÷ 单节点可用存储 × 副本数 × 1.3(膨胀系数)"估算。它的压缩比通常能到 5~10 倍,所以原始 10 TB 的日志落到磁盘大约 1.5 TB,三副本加膨胀系数约 6 TB,两台 NVMe 节点就能吃下。

StarRocks 侧:主键表因为内存常驻索引,内存是硬约束。单 BE 节点建议把主键表内存控制在物理内存的 40% 以内(128G 内存留 50G 给索引),超了就分表或改用 Duplicate Key。节点数按"并发查询峰值 × 单查询平均内存 ÷ 单节点可用内存 × 冗余系数"估算,务必预留 30% headroom 应对突增。

一个反直觉的提醒:很多团队按"当前数据量"估,忽略了增速。日志类数据常见半年翻倍,建集群时直接按 18 个月后的量预留,比半年后扩缩容迁移省心得多——而 OLAP 的扩缩容往往意味着数据重分布,比加机器本身麻烦得多。

七、7 个必踩的坑

坑 1:ClickHouse 的分布式 Join 直接 OOM。 大表 Join 大表,ClickHouse 默认可能把右表全量拉到内存。务必:

  • 维表用 Dictionary(小维表);

  • 大表关联改写成 IN 子查询或用 GLOBAL JOIN(注意 GLOBAL 语义);

  • 最根本的:在 Flink 侧预打宽,别让 ClickHouse 干这活。

坑 2:ClickHouse 的 ReplacingMergeTree 读到旧数据。 不加 FINAL 就可能读到重复/旧版本。生产上要么统一加 FINAL(接受性能损失),要么用 argMax 聚合改写:

-- 替代 FINAL 的常见写法
SELECT id, argMax(status, version), argMax(amount, version)
FROM order_table GROUP BY id;

坑 3:StarRocks 主键表内存暴涨。 Primary Key 表的主键索引常驻内存,行数过亿或主键过长时内存压力很大。对策:主键尽量短、评估内存容量、必要时改用 Duplicate Key + 外部去重。

坑 4:Flink 写入的"小批次"问题。 无论哪个引擎,Flink Sink 如果每来一条就写一次,会把引擎写垮(尤其 ClickHouse 讨厌小批量)。必须配置攒批:

// Flink -> ClickHouse/StarRocks Sink 攒批配置要点
.withBatchSize(10000)          // 攒够 1 万行再写
.withBatchIntervalMs(5000)     // 或 5 秒超时触发
.withMaxRetries(3)

同时注意:攒批会引入延迟,实时性要求和写入效率之间要权衡(这就是所谓的"mini-batch"取舍)。

坑 5:分区与分桶设计一开始就定错。 分桶数(bucket/分 shard)一旦建表就很难改。原则:

  • ClickHouse 按 dt 分区,单分区数据量控制在百万~千万行级;

  • StarRocks/Doris 分桶数 ≈ 总行数 / 单桶目标行数(建议单桶 1~10 GB),且要考虑后续扩容(分桶数最好是 BE 节点数的整数倍)。

坑 6:并发一上来就雪崩。 ClickHouse 默认 max_concurrent_queries 不高,高并发下会排队。需要调优连接池 + 服务端并发参数,且业务侧做好限流。StarRocks/Doris 稍好但同样需要压测确定并发上限。

坑 7:拿 OLAP 当 OLTP 用。 最常见也最致命。实时数仓是分析引擎,不是事务库。不要在上面做"单条记录高频更新 + 强事务"的事。如果你的需求是"每次用户操作就更新一条并立刻读",那是 HBase/Cassandra/MySQL 的活,OLAP 扛不住。

八、结论

先分形态再选型,这一步比什么都重要。日志类的形态 A 给 ClickHouse,状态宽表的形态 B 给 StarRocks 或 Doris,形态 C 的点查加一层缓存。选错形态比选错引擎严重得多,后者只是慢一点,前者是天天绕路。

真正的分水岭是 Join 和 UPSERT。单表扫描三者差距不到 2 倍,这点差距不构成决策依据;多表 Join 差 3.7 倍,UPSERT 的一致性是质的差别,这两项才是选型时该反复掂量的。

混合部署是成熟团队的常态,不是妥协。日志走 ClickHouse、宽表走 StarRocks、热点走 Redis,整体成本和体验都优于一套引擎打天下。运维复杂度也要算进成本,ClickHouse 单机友好,StarRocks 需要理解 FE/BE,Doris 最平滑,你的团队能不能养得起,比跑分重要。

还有一句可能反直觉的话:Flink 侧的攒批和预打宽,影响比引擎选型更大。很多所谓的"引擎性能问题",根子是写入姿势不对,小批次写入能把任何 OLAP 引擎写垮。

迁移的话留够时间,别压缩比对环节。双写加一致性比对加灰度切读,5~6 周是合理周期。省下的那一周,通常会用一次线上事故还回去。

先用真实数据跑一遍你的核心查询,再决定。本文的压测是通用基准,你的数据分布、查询模式、并发特征才是最终裁判。花两天做一次真实 POC,比吵一周的架有用得多。


转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/liangjinhu/article/details/167920657

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--