欢迎光临
我们一直在努力

4. Doris:存储引擎

1. 存储引擎定位与设计目标

Apache Doris 是一个 MPP 架构的实时 OLAP 数据库,其存储引擎需同时满足:

  • 高吞吐写入(支持 Kafka、Routine Load、Broker Load 等)
  • 低延迟查询(亚秒级响应)
  • 强一致性与高可用
  • 高效压缩与 I/O 优化
  • 自动运维(Compaction、副本修复、负载均衡)

为此,Doris 存储引擎采用 LSM-Tree 思想 + 列式存储 + 多版本并发控制(MVCC) 的混合架构,在 写性能、读性能、存储效率 之间取得平衡。

2. 整体架构:分层与核心组件

Doris 存储引擎运行在 BE(Backend)节点 上,核心模块如下:

 

2.1. StorageEngine(存储引擎入口)

  • 定义于 be/src/olap/storage_engine.h
  • 负责管理所有 Tablet 生命周期、数据目录映射、Compaction 调度
  • 单例模式,是 BE 存储层的“中枢神经系统”

2.2. Tablet(数据分片)

  • 最小物理存储单元,也是副本管理的基本单位
  • 一个 Partition 被划分为 N 个 Bucket,每个 Bucket = 1 个 Tablet(多副本 = 多 Replica)
  • 每个 Tablet 包含多个 Rowset(版本化数据集合)

2.3. Rowset(版本化数据集)

  • 一次导入(Load)或 Compaction 生成一个 Rowset
  • 具有 版本号(Version),如 [10-10], [11-11], [10-11](后者为合并结果)
  • 内部由 1~N 个 Segment 文件 组成
  • 支持状态机管理:UNLOADED → LOADED → UNLOADING

2.4. Segment(物理文件)

  • 默认大小 256MB(可配置)
  • 采用 自研列存格式 Segment V2
  • 包含:Data Region(数据页)、Index Region(索引页)、Footer(元数据)

3. 数据组织模型:从逻辑到物理

Doris 采用 四级数据组织模型:

层级

说明

示例

Table

逻辑表

user_behavior

Partition

逻辑分区(RANGE/LIST)

p202501, p202502

Bucket/Tablet

物理分片(Hash 分桶)

Bucket=10 → 10 个 Tablet

Replica

副本(默认 3 副本)

Tablet_123 在 BE1/BE2/BE3 各存一份

📌 关键点:

  • 查询时,FE 根据 WHERE 条件做 分区裁剪 + 桶裁剪,仅下发相关 Tablet;
  • BE 并行扫描本地 Tablet,实现 MPP 执行。

4. 写入流程:基于 LSM-Tree 的优化实践

Doris 写入借鉴 LSM-Tree,但针对 OLAP 场景深度定制:

4.1. 写入路径

  • FE → BE:分发导入任务
  • BE → MemTable:写入内存表(排序+主键去重)
  • MemTable → DeltaWriter:达阈值(128MB/5min)
  • DeltaWriter → Segment:生成不可变 Segment 文件
  • Segment → Rowset:封装为 Rowset
  • FE → BE: 两阶段提交(2PC)
  •  

    4.2. 关键优化

    • Delta Writer 机制:批量写入、按列组织,提升吞吐;
    • MemTable 排序:提前按 Sort Key 排序,利于后续 ZoneMap 和 Compaction;
    • 两阶段提交(2PC):确保多副本原子可见;
    • 异步 Flush:不阻塞前端写入。

    5. 读取流程:多级缓存 + 按需加载

    5.1. 读取路径

  • FE 生成执行计划,下发 Scan Fragment 到 BE;
  • BE 加载 Tablet 元数据(TabletMeta);
  • 遍历 Rowset 版本链,确定需读取的 Segment;
  • 按列加载:仅读取查询涉及的列;
  • Page 级跳过:利用 ZoneMap/BloomFilter 跳过无关 Data Page;
  • 解压 → 解码 → 向量化处理
  • 5.2. 多级缓存体系

    缓存类型

    内容

    默认大小

    作用

    Page Cache

    Data Page / Index Page

    机器内存 30%

    减少磁盘 I/O

    Metadata Cache

    Segment Footer / Schema

    1GB

    加速元数据访问

    PK Index Cache(MoW)

    主键索引

    可配

    加速 Unique Key 更新

    6. Compaction 机制:消除写放大,优化查询

    6.1. 为什么需要 Compaction?

    • 频繁写入产生大量小 Rowset;
    • 查询需扫描多个文件,I/O 放大;
    • 删除/更新留下无效数据(MoR 模型)。

    6.2. 两类 Compaction

    类型

    触发条件

    合并范围

    频率

    Cumulative Compaction

    小 Rowset 数量 > 阈值

    合并最近 N 个 Delta Rowset

    高频(分钟级)

    Base Compaction

    Base Rowset 太旧或太大

    合并 Base + 所有 Delta

    低频(小时级)

    6.3. 调度与资源控制

    • 通过 CompactionPermitLimiter 控制并发(令牌桶);
    • 配置示例(be.conf):

    cumulative_compaction_num_threads = 4
    base_compaction_num_threads = 2
    max_compaction_permits = 10000

    ✅ 效果:减少文件数、清理无效数据、提升查询性能。

    7. 列存格式:Segment V2 设计

    Segment V2 是 Doris 自研列存格式,采用 三段式结构:

    7.1. 结构组成

    区域

    内容

    说明

    Data Region

    列数据页(Data Page)

    按列连续存储,支持 PLAIN/RLE/DICT/BIT_SHUFFLE

    Index Region

    索引页(Shortkey, BloomFilter, Bitmap)

    与数据分离,便于快速过滤

    Footer

    元数据

    包含行数、各列偏移、编码/压缩算法、统计信息

    7.2. Page 管理

    • 默认 Page 大小:64KB
    • 每列的 Page 按行号顺序排列;
    • Footer 中的 Ordinal Index 记录行号 → Page 映射,作为“一级导航”。

    8. 行存

    8.1. 行存简介

    Doris 默认的列式存储:不适用点查场景SELECT *:需要读取所有列,每个列都要一次 IO 导致 IOPS 成为瓶颈,特别是宽表(比如上百列),尤为明显

    为解决点查场景 IOPS 的瓶颈问题:Doris 2.0.0 版本开始支持行列混存

    • 用户建表时指定开启行存后,点查每一行只需要一次 IO,在宽表列很多的情况下性能有数量级提升。
    • 原理:是在存储时增加了一个额外的列,这个列将对应行的所有列拼接起来采用特殊的二进制格式存储

    8.2. 使用语法

    建表时,在表的PROPERTIES中指定

  • 是否开启行存
  • 哪些列开启行存:3.0 之后的版本支持
  • 行存的存储压缩单元大小page_size(默认为 16KB):page 是存储读写的最小单元,即:读一行也需要产生一个 page 的 IO
  • 如果更偏向查询性能,可以配置较小的值比如 4KB 甚至更低
  • 如果更偏向存储空间,可以配置较大的值比如 64KB 甚至更高。
  • PROPERTIES (
    "store_row_column" = "true" — 是否开启行存:默认false,不开启
    — 哪些列开启行存:若开启行存,则默认针对所有列,若需要指定部分列,则设置
    "row_store_columns" = "column1,column2,column3"
    "row_store_page_size" = "16384"
    );

    8.3. 行存命中条件

    行存命中条件分成两种情况

  • 高并发主键点查需要依赖表的属性,以及查询满足以下条件
      • MOW 表:"enable_unique_key_merge_on_write" = "true"
      • 开启行存
      • 查询的时候注意where条件中,需要所有的主键等值并且是AND
  • 非主键点查,单表SELECT *查询
      • 表模型,满足以下一种即可:
        • DUPLICATE 表
        • 开启"enable_unique_key_merge_on_write" = "true"(MOW 表)且"store_row_column" = "true"
      • 符合TopN查询模式:SELECT * FROM tble [WHERE XXXXX] ORDER BY XXX LIMIT N 方括号中的是可选查询条件,注意目前只能是SELECT *,且需要命中 TopN 的延迟物化优化

    8.4. 注意事项

  • 开启行存后占用的存储空间会增加,存储空间的增加和数据特点有关,一般是原来表的 2 到 10 倍,具体空间占用需要使用实际数据测试
  • 行存的page_size对存储空间的也有影响
  • 9. 事务与一致性模型

    9.1. 事务语义

    • 单表原子性:一次导入要么全成功,要么全失败;
    • 多版本隔离(MVCC):查询基于 publish version 快照;
    • 线性一致性:通过 2PC + Version Publish 保证。

    9.2. Unique Key 模型演进

    • Merge-on-Read(MoR):读时合并,性能差;
    • Merge-on-Write(MoW)(Doris 2.0+):
      • 写时覆盖,物理只存最新版;
      • 引入 主键索引(RocksDB/MemIndex);
      • 支持 Partial Update + Sequence Column。

    10. 容错与高可用

    10.1. 副本机制

    • 每个 Tablet 默认 3 副本,分布在不同 BE;
    • FE 监控副本健康,自动触发 Clone 修复;
    • 支持 Colocation Group:强制同分片副本共置,加速 Join。

    10.2. 故障恢复

    • BE 宕机 → FE 标记 unavailable,查询自动路由到其他副本;
    • 磁盘损坏 → FE 触发副本迁移;
    • 元数据由 FE 通过 BDBJE 保证强一致。

    11. 性能调优关键点

    方向

    建议

    Tablet 设计

    单 Tablet 1~10GB,总 Tablet 数 ≈ BE 数 × 10

    Sort Key

    高频过滤字段 + 高基数放前,提升 Shortkey + ZoneMap 效果

    Compaction

    根据写入频率调整线程数,避免积压

    编码压缩

    数值列用 BIT_SHUFFLE + LZ4,低基数字符串用 DICT_ENCODING

    缓存

    增大 Page Cache(page_cache_limit

    )提升热数据查询性能

    Unique Key

    启用 MoW(enable_unique_key_merge_on_write=true)

    12. 未来演进方向

  • 存算分离:支持对象存储(S3/HDFS),通过 Cloud Cache 加速;
  • 统一存储引擎:融合 Duplicate/Aggregate/Unique 模型;
  • 二级索引:支持非主键列的独立索引;
  • 向量化 Compaction:利用 SIMD 加速数据合并;
  • 智能自治:自动索引推荐、Compaction 策略优化。
  •  

    赞(0)
    未经允许不得转载:171主机测评 » 4. Doris:存储引擎
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址