写在前面
本文是 Doris 学习笔记系列的第一篇,介绍 Apache Doris 的核心定位、FE/BE 架构、四种数据模型的差异,以及 Docker 部署和基础 SQL 操作。基于 Doris 2.1.x 版本。
一、Doris 是什么
1.1 定义
Apache Doris 是一个高性能、实时的分析型数据库(OLAP MPP 数据库),最初由百度 PALO 项目开源,2018 年进入 Apache 孵化器,2022 年成为顶级项目。
1
2
3
4
5
6
|
核心特征:
1. MPP 架构 — 大规模并行处理,查询可水平扩展
2. MySQL 协议 — 兼容 MySQL 语法,可用 MySQL 客户端直接连接
3. 实时写入 — Stream Load / Routine Load 支持秒级数据可见
4. 列式存储 — 高压缩比 + 向量化执行,OLAP 查询效率高
5. 不需要外部依赖 — FE + BE 两类节点,部署简单(不需要 Hadoop/Spark)
|
1.2 解决什么问题
1
2
3
4
5
6
7
8
9
|
传统离线数仓(Hive / HDFS + Spark):
- 数据延迟:T+1,今天看昨天的数据
- 查询慢:交互式 BI 查询几秒到几十秒
- 链路复杂:HDFS、YARN、Hive、Spark、Presto……
Doris 试图解决:
- 实时数仓:Flink/写入 → 秒级 / 分钟级数据可见
- 高并发查询:单集群可扛上千 QPS 的多维分析
- 极简运维:FE + BE 两类角色,二进制部署
|
1.3 同类产品对比
| 维度 |
Doris |
ClickHouse |
StarRocks |
Druid |
| 出身 |
百度 → Apache |
Yandex |
Doris 商业分支 |
Apache |
| SQL 兼容 |
MySQL 协议 |
自有方言 |
MySQL 协议 |
有限 |
| 实时写入 |
强(Stream Load) |
弱(合并机制) |
强 |
中 |
| JOIN 能力 |
中上(Colocate/Broadcast/Shuffle) |
强(大表 JOIN) |
强 |
弱 |
| 物化视图 |
支持 |
不支持(用 Projection) |
支持 |
支持 Rollup |
| 运维复杂度 |
简单 |
中等 |
简单 |
复杂 |
1
2
3
4
|
一句话总结:
- 选 Doris:开箱即用、运维简单、实时 + OLAP 兼顾、生态完整
- 选 ClickHouse:单表极致性能、JOIN 强大、超大规模
- 选 StarRocks:Doris 升级版,外表联邦查询更好,性能更激进
|
1.4 什么是联邦查询
联邦查询(Federated Query) 指查询引擎在查询时直接访问外部异构数据源(MySQL、Hive、Iceberg、Hudi、Kafka、ES 等),把外部表当本地表来查,甚至跨源 JOIN,不实际搬移数据。
1
2
3
4
5
6
7
8
9
10
|
订单表在 MySQL + 行为日志在 Doris 本地 + 用户画像在 Hive
→ 一条 SQL 直接 JOIN,Doris/StarRocks 自己协调怎么拉、怎么算
SELECT m.province, SUM(m.amount), COUNT(DISTINCT l.user_id)
FROM mysql_catalog.db.orders m -- MySQL 外表
JOIN local.behavior_log l USING(user_id) -- 本地表
JOIN hive_catalog.db.profile h USING(user_id) -- Hive 外表
WHERE m.dt >= '2026-06-07'
GROUP BY m.province;
|
联邦查询 vs 传统 ETL:
| 方式 |
数据搬移 |
实时性 |
维护成本 |
| ETL 进数仓 |
需要 |
有延迟(T+1 / 分钟级) |
高(多任务 + 调度 + 对账) |
| 联邦查询 |
不需要 |
实时 |
低(建个外表即可) |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
典型应用:
- 数据湖分析:直接查 S3/HDFS 上的 Iceberg/Hudi/Delta/Paimon
- 跨库报表:业务库 MySQL + 数据仓库 Hive 不想 ETL 又想统一查询
- 避免重复存储:数据湖一份,多个引擎查询
StarRocks 为什么在联邦查询上更强:
- Catalog 体系成熟早,Iceberg/Hudi/Delta Native Reader 优化激进
(直接读 Parquet/ORC,不经 Hive Metastore 中转)
- CBO 优化器对外表统计信息、谓词下推、分区裁剪做得更细
- 实测 Iceberg 大表查询比 Doris 快 1.5~2 倍
Doris 也补齐了大部分外表能力(Hive / Iceberg / JDBC Catalog 等),
但对数据湖 + 联邦这一块仍以 StarRocks 为优。
|
二、架构
2.1 整体架构
Doris 是存算耦合架构(2.1 起开始支持存算分离模式,但默认还是存算耦合)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
┌────────────────────────────────────┐
│ 客户端 / 应用 │
│ (MySQL Client / JDBC / Stream) │
└─────────────────┬──────────────────┘
│
▼
┌───────────────────────────────────────┐
│ FE (Frontend) │
│ 元数据 / SQL 解析 / 查询计划 / 调度 │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ FE-1 │ │ FE-2 │ │ FE-3 │ │
│ │Master│←→│Follw.│←→│Follw.│ │
│ └──────┘ └──────┘ └──────┘ │
└─────────────────┬─────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ BE-1 │ │ BE-2 │ │ BE-3 │
│ 存储+计算 │ │ 存储+计算 │ │ 存储+计算 │
└──────────┘ └──────────┘ └──────────┘
|
2.2 FE(Frontend)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
职责:
- 元数据管理(数据库、表、分区、副本位置)
- SQL 解析、查询优化(CBO 优化器)
- 查询计划生成与调度
- 元数据高可用(基于 BDB-JE)
角色:
- Master FE:唯一,负责元数据写入
- Follower FE:参与选主,可读元数据
- Observer FE:只读,仅用于扩展查询能力
推荐部署:
- 至少 3 个 FE(1 Master + 2 Follower),保证多数派可用
- Observer 用于读扩展,可以任意数量
|
2.3 BE(Backend)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
职责:
- 数据存储(按 Tablet 分片,副本存放)
- 查询执行(向量化执行引擎)
- 数据导入(Stream Load / Routine Load 实际处理者)
存储结构:
- 物理上:Table → Partition → Tablet → Rowset → Segment
- Tablet 是数据分片和副本管理的最小单位
- 默认每张表 3 副本(可配置)
推荐部署:
- 至少 3 个 BE(满足默认 3 副本分布)
- 每个 BE 单独部署在一台物理机 / 容器
|
2.4 一条查询的执行流程
1
2
3
4
5
6
7
8
9
|
1. 客户端发送 SQL 到任意 FE
2. FE 解析 SQL → 生成逻辑计划
3. CBO 优化器生成最优物理计划
4. FE 将计划分发到 BE 节点
5. BE 并行执行(MPP)
- 扫描节点(Scan Node):读本地数据
- 计算:过滤、聚合、JOIN
- 数据通过网络 Shuffle / Broadcast
6. 汇总结果到 FE,返回客户端
|
三、Docker 部署
最快的体验方式是用官方的 Docker 镜像跑一个单机版(1 FE + 1 BE)。
3.1 网络
1
|
docker network create doris-net
|
3.2 启动 FE
1
2
3
4
5
6
7
|
docker run -d \
--name doris-fe \
--network doris-net \
-p 8030:8030 \
-p 9030:9030 \
-e FE_SERVERS="fe1:172.20.80.2:9010:9020:9030" \
apache/doris:fe-2.1.7
|
1
2
3
4
5
|
端口说明:
- 8030:FE Web UI
- 9030:MySQL 协议端口(用 mysql 客户端连接)
- 9010:FE 内部通信
- 9020:FE 元数据编辑日志
|
3.3 启动 BE
1
2
3
4
5
6
7
|
docker run -d \
--name doris-be \
--network doris-net \
-p 8040:8040 \
-e BE_ADDR="172.20.80.3:9050" \
-e FE_SERVERS="fe1:172.20.80.2:9010:9020:9030" \
apache/doris:be-2.1.7
|
3.4 添加 BE 到集群
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 用 mysql 客户端连接 FE
mysql -h 127.0.0.1 -P 9030 -uroot
# 默认无密码
# 查看 FE 状态
SHOW FRONTENDS;
# Expected:Alive = true
# 查看 BE 状态(首次为空)
SHOW BACKENDS;
# 添加 BE
ALTER SYSTEM ADD BACKEND "172.20.80.3:9050";
# 再次查看
SHOW BACKENDS;
# Expected:Alive = true,HeartbeatPort = 9050
|
3.5 Web UI
访问 http://localhost:8030 查看集群状态:
- System Info → 看 FE / BE 列表
- PlayGround → 直接在网页上跑 SQL
四、数据模型
Doris 提供 4 种数据模型,区别在于"同一主键(或维度)的数据如何处理"。选对模型是 Doris 表设计的核心。
4.1 明细模型(Duplicate Key)
最简单:所有写入的数据原样保留,不合并。适合日志、明细数据。
1
2
3
4
5
6
7
8
9
10
|
CREATE TABLE event_log (
event_time DATETIME NOT NULL,
user_id BIGINT,
event_type VARCHAR(32),
page VARCHAR(128),
duration INT
)
DUPLICATE KEY(event_time, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 8
PROPERTIES("replication_num" = "1");
|
1
2
3
4
|
特点:
- DUPLICATE KEY 仅用于数据排序,不合并
- 写入即可查,无需任何聚合
- 适合:原始日志、明细数据
|
4.2 聚合模型(Aggregate)
预先声明聚合方式(SUM / MIN / MAX / REPLACE / BITMAP_UNION / HLL_UNION),相同 Key 的数据自动合并。
1
2
3
4
5
6
7
8
9
10
|
CREATE TABLE user_metric_agg (
dt DATE,
user_id BIGINT,
pv BIGINT SUM, -- 求和
uv BITMAP BITMAP_UNION, -- BITMAP_UNION 计数
stay_sec INT MAX -- 取最大
)
AGGREGATE KEY(dt, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 8
PROPERTIES("replication_num" = "1");
|
1
2
3
4
5
6
7
8
|
应用场景:
- 数据仓库的汇总表(按天/小时聚合)
- UV 统计(BITMAP_UNION)
- 适合:查询前数据需要聚合的场景
注意:
- 写入相同 Key 多次,只保留聚合结果
- 读取时自动按 Key 再做一次合并
|
4.3 唯一模型(Unique Key,旧版)
保证主键唯一,后写覆盖前写。适合主键更新场景,但读性能不如主键模型。
1
2
3
4
5
6
7
8
9
|
CREATE TABLE user_profile (
user_id BIGINT,
name VARCHAR(32),
age INT,
updated_at DATETIME
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 8
PROPERTIES("replication_num" = "1");
|
1
2
3
4
5
6
7
8
|
特点(旧版 Unique):
- 实现:Read 时合并(Merge on Read)
- 写入:多次写同主键,最后写入的"胜出"
- 适合:维度表、用户画像(不频繁更新)
性能提示:
- 旧版 Unique 查询会触发合并,性能不如主键模型
- 新版本建议优先用 Primary Key 模型
|
4.4 主键模型(Primary Key,新版推荐)
Doris 1.2+ 引入,真正支持高效 UPSERT 和部分列更新。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
status VARCHAR(16),
amount DECIMAL(10, 2),
created_at DATETIME
)
PRIMARY KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 8
PROPERTIES(
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true"
);
|
1
2
3
4
5
6
7
8
9
10
|
特点:
- 主键唯一约束,UPSERT 自动覆盖
- enable_unique_key_merge_on_write = true 时
→ 写时合并(Copy on Write),读性能接近明细模型
- 支持部分列更新(UPDATE col1 = ... WHERE pk = ...)
应用场景:
- 订单表(订单状态机:created → paid → shipped)
- 实时画像(频繁更新某些字段)
- CDC 同步(MySQL binlog 实时写入)
|
4.5 四种模型对比
| 模型 |
主键语义 |
合并时机 |
适合场景 |
UPSERT 支持 |
| Duplicate |
仅排序 |
不合并 |
日志、明细 |
❌ |
| Aggregate |
聚合维度 |
写入 + 读取 |
汇总表、UV/PV |
❌(覆盖语义) |
| Unique(旧) |
唯一 |
读取时合并 |
维度表 |
✅(但慢) |
| Primary(新) |
唯一 |
写入时合并 |
订单、画像、CDC |
✅(高性能) |
1
2
3
4
5
|
选型建议:
- 日志/原始明细 → Duplicate
- 已经按维度聚合过的统计表 → Aggregate
- 需要主键更新(订单/画像/CDC) → Primary Key
- 旧版 Unique 除非兼容老系统,否则用 Primary 替代
|
五、基础 SQL 操作
5.1 建库
1
2
|
CREATE DATABASE demo;
USE demo;
|
5.2 建表(示例:明细模型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
CREATE TABLE access_log (
dt DATE,
ts DATETIME,
user_id BIGINT,
page VARCHAR(128),
cost_ms INT
)
DUPLICATE KEY(dt, ts, user_id)
PARTITION BY RANGE(dt) (
PARTITION p20260601 VALUES [('2026-06-01'), ('2026-06-02')),
PARTITION p20260602 VALUES [('2026-06-02'), ('2026-06-03'))
)
DISTRIBUTED BY HASH(user_id) BUCKETS 4
PROPERTIES("replication_num" = "1");
|
1
2
3
4
|
关键概念:
PARTITION(分区):按 dt 按天分区,便于按时间裁剪和管理
BUCKETS(分桶):分区内按 user_id 哈希分桶,控制并行度
副本:replication_num = 1(单机测试,生产至少 3)
|
5.3 数据导入
Doris 提供多种导入方式,最常用的是 Stream Load(HTTP 推送):
1
2
3
4
5
|
# 通过 curl 推送 CSV
curl -u root: -H "label:load_001" \
-H "column_separator:," \
-T data.csv \
http://localhost:8030/api/demo/access_log/_stream_load
|
1
2
3
4
5
|
-- 也可以在 SQL 客户端用 INSERT 插入
INSERT INTO access_log VALUES
('2026-06-01', '2026-06-01 10:00:00', 1001, '/home', 12),
('2026-06-01', '2026-06-01 10:01:00', 1001, '/list', 35),
('2026-06-01', '2026-06-01 10:02:00', 1002, '/home', 10);
|
1
2
3
4
5
|
导入方式对比:
Stream Load — HTTP 推送,适合小批量 / 实时写入
Routine Load — 持续消费 Kafka,做实时数仓
Broker Load — 从 HDFS / S3 批量导入
INSERT INTO SELECT — 从一张表导到另一张表
|
5.4 查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
-- 普通查询,标准 SQL
SELECT * FROM access_log ORDER BY ts DESC LIMIT 10;
-- 按天聚合
SELECT dt, COUNT(*) AS pv, COUNT(DISTINCT user_id) AS uv
FROM access_log
GROUP BY dt
ORDER BY dt;
-- 窗口函数
SELECT
user_id,
dt,
cost_ms,
SUM(cost_ms) OVER (PARTITION BY user_id ORDER BY dt) AS cum_cost
FROM access_log;
-- 查看执行计划
EXPLAIN
SELECT dt, COUNT(*) FROM access_log GROUP BY dt;
|
5.5 表管理
1
2
3
4
5
6
7
8
9
10
11
12
|
-- 动态新增分区(按天滚动)
ALTER TABLE access_log
ADD PARTITION p20260603 VALUES [('2026-06-03'), ('2026-06-04'));
-- 查看分区
SHOW PARTITIONS FROM access_log;
-- 修改副本数
ALTER TABLE access_log SET ("default.replication_num" = "3");
-- 删除分区(不影响其他分区)
DROP PARTITION p20260601;
|
六、小结
本文学习了 Doris 的基础:
- Doris 是什么,与 ClickHouse / StarRocks 的差异
- FE / BE 架构,存算耦合的设计
- Docker 部署单机版
- 四种数据模型(明细、聚合、唯一、主键)的差异和选型
- 基础 SQL:建库建表、导入、查询、分区管理
下一篇将学习 Doris 的进阶实战:表设计、索引、查询优化、物化视图和典型应用场景。