DD 数据开发学习站DATA ENGINEERING
模块 06 · OLAP 与湖仓

Doris 与 StarRocks

FE/BE 架构、数据模型、物化视图与 OLAP 选型对比。

难度 · 进阶 约 35 分钟 选学 练习 10 项 计划内 刷题 0/3
建议先学:ClickHouse
本页目录 · 17 节

本章目标:能讲清 Doris 的 FE/BE 架构、三种数据模型、分区分桶和物化视图,能写建表、upsert、查询 SQL,并比较 Doris、ClickHouse、StarRocks。 前置知识:会写聚合 SQL;建议先读 ClickHouse 入门。

1. 为什么需要它

ClickHouse 单表扫描极快,但复杂多表 join、高并发点查和频繁 upsert 不是它的强项。国内很多公司的实时数仓需要:

  • 用 MySQL 协议连接,现有 BI 工具和 JDBC 直接可用;
  • 支持多表 join 和高并发查询;
  • 支持按主键 upsert,承接 CDC 变更;
  • 能实时写入,也能做明细和汇总。

Doris 是这类 MPP OLAP 引擎的代表。它不替代 MySQL 的交易能力,也不一定在所有单表扫描上快过 ClickHouse,选型要看查询模式。


2. 核心概念

2.1 架构:FE 和 BE

客户端(MySQL 9030) → FE(解析/优化/调度,leader/follower/observer)
                          ↓ 分发执行计划
                    BE(存储 + 计算,多台,数据分片和副本)
  • FE(Frontend):管元数据、解析 SQL、生成执行计划、调度查询。生产通常部署 3 个 FE,leader 写元数据,follower 参与选举,observer 只读扩展;元数据存在 FE 的 BDBJE 里,不依赖外部 ZooKeeper。
  • BE(Backend):存数据、执行计算、做 compaction。数据按 tablet 分布在多个 BE 上。
  • 外部访问:FE 提供 MySQL 协议端口 9030,HTTP 端口 8030(Stream Load 等),BE 也有自己的端口;查询是 MPP 模式,多个 BE 并行执行后由 FE 汇总。

2.2 三种数据模型

模型 特点 适用
Duplicate 保留所有行,不做聚合 原始日志、明细、需要保留每一次变更
Aggregate 相同 key 的 value 列按聚合函数合并 预聚合指标、报表
Unique 相同 key 只保留一行,新数据覆盖旧数据 CDC 同步、维表、订单最新状态

Duplicate 模型用 DUPLICATE KEY(...),不聚合;Aggregate 模型用 AGGREGATE KEY(...),value 列声明 SUM 等聚合方式。下面以 Unique 为例:

CREATE TABLE dwd_order (
  order_id VARCHAR(32),
  customer_id VARCHAR(32),
  order_status VARCHAR(16),
  order_amount DECIMAL(18, 2)
) ENGINE = OLAP
UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 16
PROPERTIES ("enable_unique_key_merge_on_write" = "true");

注意:

  • key 列必须放在最前面,且不能为空;
  • Aggregate 模型的 value 列要声明聚合方式(SUM、MAX、REPLACE 等);
  • Unique 模型默认 merge-on-read,开启 merge-on-write 后查询性能更好,写入成本略高;
  • Duplicate 模型不聚合,查明细最快,但数据量最大。

2.3 分区、分桶和副本

表 → 分区 p201808 / p201809
      每个分区 → bucket 0..N → tablet → BE1/BE2/BE3 副本
  • 分区(Partition):通常按日期范围(RANGE)或枚举(LIST),用于分区裁剪和生命周期管理;也支持动态分区自动建分区。
  • 分桶(Bucket):分区内按 hash 再切分,决定并行度和数据分布;DISTRIBUTED BY HASH(order_id) BUCKETS 16。
  • Tablet:分区 × 分桶的最小数据单元,是副本和 compaction 的基本单位。
  • 副本:每个 tablet 默认 3 副本,分布在不同 BE;本地单机练习可设 1,生产不要。

分桶数不是越多越好:tablet 太小会增加元数据和 compaction 压力,太大则并行度不足。常见目标是单个 tablet 几 GB,结合数据量和 BE 数量调整。

2.4 物化视图

Doris 的物化视图(异步 MV)把聚合结果预计算并存储,查询时优化器可以透明改写。它适合固定维度的报表,例如“按品类汇总 GMV”。

CREATE MATERIALIZED VIEW mv_category_gmv
BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL
DISTRIBUTED BY HASH(category) BUCKETS 4
PROPERTIES ("replication_num" = "1")
AS
SELECT category, SUM(gmv) AS total_gmv
FROM dws_trade_minute
GROUP BY category;

REFRESH MATERIALIZED VIEW mv_category_gmv COMPLETE;

REFRESH COMPLETE 全量刷新,适合小结果集;数据量大时可用分区增量刷新。MV 不是“建完自动实时更新”,刷新策略要按业务延迟要求配。

2.5 Doris、ClickHouse、StarRocks 对比

维度 ClickHouse Doris StarRocks
定位 单表扫描极快 MPP、实时数仓、MySQL 生态 MPP、复杂 join 和高并发
多表 join 较弱,需预打宽 支持 支持,CBO 较成熟
高并发点查 一般 较好 较好
实时 upsert 弱(Replacing 最终一致) Unique Key 原生支持 Primary Key 模型
协议 自有协议/HTTP MySQL 协议 MySQL 协议
运维 相对复杂 中等 中等

三者都是优秀引擎,选型看团队、查询模式、数据规模和运维能力。


3. 动手示例

Doris 本地部署比单机 ClickHouse 复杂,通常用官方 Docker 快速开始、公司测试集群或云服务。SQL 通过 MySQL 客户端连接 FE:mysql -h 127.0.0.1 -P 9030 -uroot。

建库和分钟汇总表:

CREATE DATABASE IF NOT EXISTS realtime;
USE realtime;

CREATE TABLE dws_trade_minute (
  window_start DATETIME, category VARCHAR(32),
  order_cnt BIGINT, gmv DECIMAL(18, 2)
) ENGINE = OLAP
UNIQUE KEY(window_start, category)
PARTITION BY RANGE(window_start) (
  PARTITION p201808 VALUES <input type="checkbox" class="task sql-task" data-k="sql:lc:196" data-label="(&#x27;2018-08-01 00:00:00&#x27;), (&#x27;2018-09-01 00:00:00&#x27;))
)
DISTRIBUTED BY HASH(category) BUCKETS 8
PROPERTIES (
  &quot;replication_num&quot; = &quot;1&quot;,
  &quot;enable_unique_key_merge_on_write&quot; = &quot;true&quot;
);

写入和查询:

INSERT INTO dws_trade_minute VALUES
(&#x27;2018-08-01 10:00:00&#x27;,&#x27;health_beauty&#x27;,12,35988.00),
(&#x27;2018-08-01 10:00:00&#x27;,&#x27;books_general_interest&#x27;,30,1770.00),
(&#x27;2018-08-01 10:01:00&#x27;,&#x27;health_beauty&#x27;,8,23992.00);

SELECT category, SUM(order_cnt) AS order_cnt, SUM(gmv) AS gmv
FROM dws_trade_minute
WHERE window_start &gt;= &#x27;2018-08-01 10:00:00&#x27;
  AND window_start &lt; &#x27;2018-08-01 11:00:00&#x27;
GROUP BY category
ORDER BY gmv DESC;

Unique Key 的 upsert 效果:再插入同一 (window_start, category),旧行会被覆盖,查询不会翻倍。

INSERT INTO dws_trade_minute VALUES (&#x27;2018-08-01 10:00:00&#x27;,&#x27;health_beauty&#x27;,13,38987.00);
-- 再查这一行,gmv 已被覆盖为 38987.00,而不是两条相加

物化视图的建表语句见 2.4,建好后手动刷新并查询:

REFRESH MATERIALIZED VIEW mv_category_gmv COMPLETE;
SELECT * FROM mv_category_gmv ORDER BY total_gmv DESC;

生产上用 Stream Load 或 Flink Doris Connector 导入,同一个 label 重复提交不会重复导入,这是导入层的幂等手段。


4. 常见坑

  1. key 列选错。Unique 模型按 key 覆盖,key 选订单 ID 还是订单 ID + 商品 ID,决定了更新粒度。
  2. 分区过多。按天分区一年 365 个分区,小数据量时元数据压力大;按数据量选择天/月。
  3. 分桶随意设。桶太少并行度不够,桶太多小文件多;按 tablet 大小估算。
  4. Aggregate 模型期待明细更新。它只保留聚合结果,原始明细变化无法直接改;需要明细就用 Duplicate 或 Unique。
  5. Unique 模型以为查询自动去重。merge-on-read 需要合并,merge-on-write 也要等导入版本生效;理解版本机制。
  6. MV 建完不刷新。异步 MV 不会自动实时更新,要配刷新策略。
  7. 单机配置直接上线。生产至少 3 副本,否则 BE 挂了数据不可用;也不要把 Doris 当 MySQL 事务库,它不适合高频单行事务更新。

5. 在线练习

题目 考点
[196. 删除重复的电子邮箱"> [('2018-08-01 00:00:00'), ('2018-09-01 00:00:00'))
)
DISTRIBUTED BY HASH(category) BUCKETS 8
PROPERTIES (
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true"
);

写入和查询:

```sql
INSERT INTO dws_trade_minute VALUES
('2018-08-01 10:00:00','health_beauty',12,35988.00),
('2018-08-01 10:00:00','books_general_interest',30,1770.00),
('2018-08-01 10:01:00','health_beauty',8,23992.00);

SELECT category, SUM(order_cnt) AS order_cnt, SUM(gmv) AS gmv
FROM dws_trade_minute
WHERE window_start >= '2018-08-01 10:00:00'
  AND window_start < '2018-08-01 11:00:00'
GROUP BY category
ORDER BY gmv DESC;

Unique Key 的 upsert 效果:再插入同一 (window_start, category),旧行会被覆盖,查询不会翻倍。

INSERT INTO dws_trade_minute VALUES ('2018-08-01 10:00:00','health_beauty',13,38987.00);
-- 再查这一行,gmv 已被覆盖为 38987.00,而不是两条相加

物化视图的建表语句见 2.4,建好后手动刷新并查询:

REFRESH MATERIALIZED VIEW mv_category_gmv COMPLETE;
SELECT * FROM mv_category_gmv ORDER BY total_gmv DESC;

生产上用 Stream Load 或 Flink Doris Connector 导入,同一个 label 重复提交不会重复导入,这是导入层的幂等手段。


4. 常见坑

  1. key 列选错。Unique 模型按 key 覆盖,key 选订单 ID 还是订单 ID + 商品 ID,决定了更新粒度。
  2. 分区过多。按天分区一年 365 个分区,小数据量时元数据压力大;按数据量选择天/月。
  3. 分桶随意设。桶太少并行度不够,桶太多小文件多;按 tablet 大小估算。
  4. Aggregate 模型期待明细更新。它只保留聚合结果,原始明细变化无法直接改;需要明细就用 Duplicate 或 Unique。
  5. Unique 模型以为查询自动去重。merge-on-read 需要合并,merge-on-write 也要等导入版本生效;理解版本机制。
  6. MV 建完不刷新。异步 MV 不会自动实时更新,要配刷新策略。
  7. 单机配置直接上线。生产至少 3 副本,否则 BE 挂了数据不可用;也不要把 Doris 当 MySQL 事务库,它不适合高频单行事务更新。

5. 在线练习

题目 考点
196. 删除重复的电子邮箱 唯一键去重
176. 第二高的薪水 排序取值
185. 部门工资前三高的所有员工 排名函数

自测:

  • 能根据“是否要保留明细、是否要更新、是否要预聚合”选择 Duplicate、Unique、Aggregate 模型。
  • 能解释分区和分桶分别解决什么问题。
  • 能说出 Unique Key 模型下 update、delete 的处理方式。
  • 能描述 MySQL CDC 同步到 Doris 的关键步骤。

6. 面试延伸

面试题:Doris 的三种数据模型怎么选?

是什么:Duplicate 保留所有行,Aggregate 按 key 预聚合,Unique 按 key 覆盖。

解决什么问题:不同场景对明细保留、写入方式和查询性能要求不同。

我的实操边界:我在本地/测试环境写过三种模型的建表 SQL,理解 key 和 value 列;没有在生产集群做过 compaction 和副本调优。选择标准:要明细用 Duplicate;固定聚合指标用 Aggregate;CDC/最新状态用 Unique。Unique 的 key 粒度决定更新粒度。

面试题:分区和分桶的区别?

是什么:分区按时间/枚举切数据目录,分桶在分区内按 hash 再切 tablet。

解决什么问题:分区做裁剪和生命周期管理,分桶做并行和均匀分布。

我的实操边界:我建过按日期分区、按主键分桶的表,理解 tablet 和副本;没有处理过数据倾斜导致的分桶重设计。分区键通常选日期,分桶键选高基数且常用于 join/过滤的字段;桶数按数据量和 BE 数估算。

面试题:Doris、ClickHouse、StarRocks 怎么选?

是什么:三者都是 OLAP 引擎,ClickHouse 偏单表扫描,Doris 和 StarRocks 偏 MPP 多表和高并发。

解决什么问题:不同查询模式需要不同引擎:日志大宽表、实时 upsert、复杂 join、高并发报表。

我的实操边界:我在本地跑过 ClickHouse 和 Doris 的基础 SQL,StarRocks 只了解定位;没有做过生产选型和压测。选型看查询模式、并发、数据量、MySQL 生态和团队运维。单表日志扫描优先 ClickHouse;实时 upsert 和多表 join 优先 Doris/StarRocks。


7. 本章验收

  • 能画出 FE、BE、tablet、副本的关系,并说出 9030 和 8030 端口的作用。
  • 能比较 Duplicate、Aggregate、Unique 三种模型,并为三个场景选对模型。
  • 能写出按日期分区、按主键分桶的 Doris 建表 SQL。
  • 能解释 Unique Key 的 upsert 如何保证重放不翻倍。
  • 能创建物化视图并手动刷新,说明它和实时更新的区别。
  • 能说出 Doris、ClickHouse、StarRocks 的定位差异,不把“最快”当唯一标准。
本章任务0 / 10
本页目录