开篇核心:为什么要吃透Doris基础架构?
在上一篇中,我们了解了Doris在数据架构中的定位、核心特性及应用场景,而要真正用好Doris、做好调优与故障排查,基础架构与核心概念是必经之路。
Doris的高性能、高可用、可扩展性,本质源于其简洁而强大的MPP架构——仅由FE(Frontend)和BE(Backend)两类核心进程组成,再配合科学的分区分桶策略,实现存储与计算的高效协同。本文将深入拆解FE与BE的角色分工、交互机制,详解分区分桶的设计逻辑与实践技巧,帮你从底层理解Doris的运行原理,为后续调优、建模打下坚实基础。
1. 架构体系:FE 与 BE 的角色分工与交互机制
Doris 采用典型的 主从式 MPP 架构,由两类核心进程组成:
1.1. FE(Frontend):元数据与查询协调层
职责:
- 接收用户 SQL 请求(通过 MySQL 协议兼容接口)
- 负责 SQL 解析、语法校验、逻辑计划生成、优化器执行(基于 RBO/CBO)
- 生成物理执行计划并分发给 BE 节点
- 管理集群元数据(表结构、分区信息、副本状态等)
- 协调 Schema 变更、数据导入、副本修复等 DDL/DML 操作
高可用性:FE 支持多实例部署,通过 BDBJE(Berkeley DB Java Edition) 实现元数据强一致性复制,其中仅有一个 Leader FE 处理写请求,其余为 Follower/Observer。
无状态查询入口:所有 FE 都可接收查询请求,非 Leader FE 会将写操作转发给 Leader。
1.2. BE(Backend):存储与计算执行层
职责:
- 存储实际数据(列式存储)
- 执行由 FE 下发的物理执行计划片段(Fragment)
- 提供本地谓词下推、向量化执行、预聚合等优化能力
- 管理本地 Tablet(数据分片)的生命周期、副本同步、Compaction 等
完全对等:所有 BE 节点地位平等,无主从之分,支持水平扩展。
本地计算 + 网络 Shuffle:BE 在本地处理数据,并通过 百度 RPC与其他 BE 交换中间结果(如 Join、Agg 的 Shuffle 阶段)。
1.3. FE 与 BE 的交互机制
- 心跳机制:
-
- BE 定期向 FE 发送心跳(默认 5 秒),汇报自身状态(磁盘、负载、Tablet 列表等);
- FE 通过心跳监控 BE 健康状态。
- 任务下发:FE 将查询计划拆分为多个 Fragment,通过 Thrift 或 BRPC 协议下发给相关 BE。
- 元数据同步:FE 在创建表、分区、导入任务时,会通知 BE 创建或删除对应的 Tablet 目录。
- 副本管理:FE 决定 Tablet 副本分布策略,BE 负责具体的数据复制(通过 Clone 或 Publish 机制)。
✅ 关键点:FE 是“大脑”,负责调度与元数据;BE 是“肌肉”,负责存储与计算。二者解耦,可独立扩缩容。
2. 核心进程深度解析
2.1. FE 的元数据结构与高可用机制
- 元数据模型:
-
- 所有元数据(Database、Table、Partition、Index、Tablet、Replica 等)以对象树形式组织,持久化到 BDBJE。
- 元数据变更(如 CREATE TABLE)先写入 BDBJE 日志(Journal),达成多数派共识后才生效。
- BDBJE 高可用机制:
-
- BDBJE 是一个嵌入式、事务型、基于日志的 Java KV 存储引擎。
- FE 集群中,Leader 将元数据变更写入本地 BDBJE,并同步到 Follower 的 BDBJE 实例。
- 采用 Paxos 协议(实际是 Quorum-based Replication)保证强一致性。
- 日志(Edit Log)按序编号(Journal ID),Follower 通过重放日志保持与 Leader 一致。
- 主节点选举(Leader Election):
-
- 基于 BDBJE 自带的 Election 机制(类似 Raft 的 Term + Vote)。
- 当 Leader 宕机,剩余 Follower 通过 BDBJE 的 Group Membership 协商新 Leader。
- 选举过程自动完成,通常在 10~30 秒内恢复服务。
- Observer 不参与选举,仅用于读扩展。
⚠️ 注意:FE 元数据必须保持强一致,因此不支持异步复制或最终一致性。
2.2. BE 的存储与数据管理机制
- 列式存储格式(Column-oriented Storage):
-
- 数据按列存储,每个列独立编码(如 BIT_SHUFFLE、LZ4、ZSTD)、压缩。
- 支持高效谓词下推、向量化扫描(SIMD 加速)。
- 文件格式为 Segment File(类似 Parquet,但 Doris 自研)
- 索引结构:
-
- 前缀稀疏索引(Short Key Index):对排序键(Sort Key)构建稀疏索引,加速范围查询。
- Min/Max Zone Map:每个 Data Page 记录 min/max 值,用于跳过无关数据块。
- Bloom Filter(可选):对高基数列加速等值查询。
- Bitmap Index(针对低基数列):加速 IN、等值过滤。
- Tablet(数据分片)管理:
-
- Tablet 是 Doris 最小物理存储单元,也是副本管理的基本单位。
- 一个 Partition 被划分为 N 个 Bucket,每个 Bucket 对应一个 Tablet(多副本即多个 Replica)。
- BE 本地维护 Tablet 的版本链(Version Chain),支持 MVCC(多版本并发控制)。
- Compaction 机制:自动合并小文件(Base + Delta → New Base),减少读放大。
✅ Tablet = 分桶 × 副本,是 Doris 并行处理和容错的基础。
3. 数据分布:分区(Partition)与分桶(Bucket)
Doris 的数据分布采用 两级分片模型:
3.1. 分区(Partition)
分区:逻辑分片,用于数据生命周期管理与剪枝
设计哲学:
- 按时间(如天、月)或业务维度(如地域)划分,便于 分区裁剪(Partition Pruning)。
- 支持动态增删分区(
ADD/DROP PARTITION),适合冷热分离、TTL 管理。
类型:
- Range Partition(最常用):
PARTITION BY RANGE(date),支持时间类型、整数类型 - List Partition(较少用):
PARTITION BY LIST(city),支持时间、整数、布尔、字符、字符串类型
分区模式
- 手动分区:用户手动创建分区(如建表时指定或通过
ALTER语句增加) - 动态分区:系统根据时间调度规则自动创建分区,但写入数据时不会按需创建分区
-
- 按照设定的规则,滚动添加、删除分区,从而实现对表分区的生命周期管理(TTL),减少数据存储压力
- 自动分区:数据写入时,系统根据需要自动创建相应的分区,使用时注意脏数据,以免生成过多的分区
-
- 主要用于解决:数据实际可能涉及的分区无法预先掌握,或预期所需分区数量过大,手动创建过于繁琐
选择策略:
- 高频查询条件包含分区字段(如
WHERE dt = '2025-01-01') - 单分区数据量建议控制在 10GB ~ 100GB(避免过大导致 Compaction 压力)
注意事项:
NULL分区:PARTITION列默认必须为NOT NULL列,如果需要使用NULL列,应设置
-
session variable allow_partition_column_nullable = true- 针对
NULL值
-
-
- Range 分区:被划归最小的
LESS THAN分区,没有LESS THAN分区时,无法插入 - List 分区:支持真正的
NULL分区,VALUES IN (NULL)
- Range 分区:被划归最小的
-
3.2. 分桶(Bucket / Distribution)
分桶:物理分片,用于并行计算与数据均衡
设计哲学:
- 将一个 Partition 再哈希打散为多个 Bucket(即 Tablet),实现 数据均匀分布 与 MPP 并行扫描。
- 桶数一旦设定,不可动态修改(需重建表)。
分桶键(Bucket Key)选择:
- 应选择 高基数、均匀分布 的列(如
user_id、order_id) - 若常做
Join,建议将 Join Key 作为分桶键,实现 Colocation Join(避免 Shuffle) - DUPLICATE 表:分桶键可选任何 Key 列与 Value 列
- AGGREGATE 和 UNIQUE 表:分桶键必须是 Key 列,为了保证逐渐的聚合性
对查询性能的影响:
- 桶数过少 → 并行度低,单 BE 负载高
- 桶数过多 → 元数据膨胀,小文件问题严重
- 经验法则:总 Tablet 数 ≈ BE 节点数 × 10(如 10 节点集群,总桶数 80~120)
分桶键的选择规则
- 查询过滤条件:使用查询中的过滤条件进行 Hash 分桶,有助于数据的剪裁,减少数据扫描量;
- 高基数列:选择高基数(唯一值较多)的列进行 Hash 分桶,有助于数据均匀的分散在每一个分桶中;
- 高并发点查场景:建议选择单列或较少列进行分桶。点查可能仅触发一个分桶扫描,不同查询之间触发不同分桶扫描的概率较大,从而减小查询间的 IO 影响。
- 大吞吐查询场景:建议选择多列进行分桶,使数据更均匀分布。若查询条件不能包含所有分桶键的等值条件,将增加查询吞吐,降低单个查询延迟。
分桶数量:
- 一个 bucket 会被存储为一个物理文件(tablet),一个表的 Tablet 数量 = 分区数 × 分桶数,一旦指定 Partition 的数量,便不可更改
- 大小原则:建议一个 tablet 的大小在 1-10G 范围内。
-
- 过小的 tablet 可能导致聚合效果不佳,增加元数据管理压力;
- 过大的 tablet 则不利于副本迁移、补齐,且会增加 Schema Change 操作的失败重试代价
- 百兆左右的维表 Tablet 数量控制在 3-5 个,保证一定的并发数也不会产生过多的小文件
- 数量原则:在不考虑扩容的情况下,一个表的 tablet 数量建议略多于整个集群的磁盘数量。
例如,假设有 10 台 BE 机器,每个 BE 一块磁盘,可以按照以下建议进行数据分桶:
|
单表大小 |
建议分桶数量 |
|
500MB |
4-8 个分桶 |
|
5GB |
6-16 个分桶 |
|
50GB |
32 个分桶 |
|
500GB |
建议分区,每个分区 50GB,每个分区 16-32 个分桶 |
|
5TB |
建议分区,每个分区 50GB,每个分区 16-32 个分桶 |
3.3. 分区 + 分桶的协同效应
CREATE TABLE sales (
dt DATE,
user_id BIGINT,
amount DECIMAL(10,2)
)
PARTITION BY RANGE(dt) (
PARTITION p202501 VALUES LESS THAN ("2025-02-01"),
PARTITION p202502 VALUES LESS THAN ("2025-03-01")
)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
查询 WHERE dt = '2025-01-15' AND user_id = 123:
- 分区裁剪:只扫描
p202501 - 桶定位:
hash(123) % 10→ 定位到具体 Tablet - 本地扫描:仅访问 1 个 BE 上的 1 个 Tablet,极致高效
✅ 黄金法则:分区用于“大范围过滤”,分桶用于“细粒度并行 + Join 优化”。
结尾总结 + 系列预告
本文核心总结:Doris 的基础架构核心是“FE 调度 + BE 执行”的解耦设计,配合分区(逻辑分片)与分桶(物理分片)的两级策略,既保证了高可用、可扩展性,又实现了查询性能的优化。掌握 FE/BE 的交互机制、Tablet 管理逻辑,以及分区分桶的设计技巧,是后续进行 Doris 表设计、性能调优的核心前提。
💡 关键提醒:分区分桶的设计没有“标准答案”,需结合业务数据量、查询模式灵活调整,文中的经验值可作为落地参考,避免踩坑。
727



被折叠的 条评论
为什么被折叠?



