Doris 实战与架构

分析型数据库 / 源码阅读 / LESSON 15

Doris Cloud 存算分离

对比 Doris Classic 与 Cloud 存算分离架构,理解对象存储、缓存和弹性计算的权衡。

阅读时间
40 分钟 | **前置阅读**: 所有前 14 个文档 (基于 Classic 版本)
学习路径
Doris 实战与架构
内容来源
Doris 深度笔记

预计阅读时间: 40 分钟 | 前置阅读: 所有前 14 个文档 (基于 Classic 版本) 下一次阅读: doc-16(监控与排障), doc-17(表设计与建模)


1. Cloud 架构概览

Classic vs Cloud——维度对比

Classic (存算一体):
  FE ──► BE-1: CPU + 本地 NVMe (计算 + 存储混合)
         BE-2: CPU + 本地 NVMe
         BE-3: CPU + 本地 NVMe
  问题: 加 BE = 加计算 AND 存储 → 不能独立扩缩容

Cloud (存算分离):
  FE ──► ComputeCluster (计算, 无本地存储)
         └── BE (无状态计算节点, 弹性伸缩)
              │
              ▼ FileCache (本地 NVMe 缓存, 可选)
              │
  MetaService ◄──► Object Storage (S3/HDFS 兼容, 所有数据)

核心组件

组件缩写角色
Meta ServiceMS替代 FE 的部分职责: Tablet 元数据、Txn 管理
Global Control ServiceGCS集群资源调度, Cluster 生命周期
Compute ClusterCC计算节点组 (一组 BE, 无数据, 可动态伸缩)
Object Storage-S3/HDFS 兼容的对象存储

源码导航

文件关键类/方法职责
be/src/io/fs/s3_file_system.hS3FileSystemS3 文件系统抽象
be/src/io/fs/local_file_system.hLocalFileSystem (NVMe)本地文件系统
be/src/io/cache/file_cache.hFileCacheFileCache 接口
be/src/cloud/Cloud 专用 FE 逻辑Cloud 集群管理
cloud/Cloud 部署/配置Cloud 工程目录

Doris Cloud 存算分离 图 01

2. 存算分离的核心变化

写入路径

Classic: Client → FE → BE (写本地磁盘 + MemTable + Flush)
Cloud:   Client → FE → BE (写 MemTable + Flush → 上传 Segment 到 S3)
          BE 本地: FileCache (最新 Rowset 的缓存, NVMe)
          S3: 所有 Rowset 的持久化存储

读取路径

Classic: BE Scanner → 本地磁盘 Rowset Segment
Cloud:   BE Scanner → FileCache (缓存命中)?
         FileCache Miss → S3 (通过 BE I/O 层读取)
           → FileReader → S3Reader (HTTP Range GET)
             → 缓存到 FileCache → 返回 Block

查询层

查询的执行(Optimizer/Pipeline/向量化)与 Classic 几乎相同。差异仅在 Scanner 的数据源 (本地 vs S3), 大部分代码在 be/src/io/ 层抽象。


3. FileCache——本地 NVMe 缓存层

缓存策略

FileCache (/mnt/nvme/cache/):
├── LRU (默认): 最近最少使用的 Segment 被淘汰
├── TTL: 缓存条目过期时间
└── 最大容量: 整个 NVMe 盘 (或限制比例)

读写流程

写: Segment Flush 到本地 NVMe → 异步上传到 S3
   → FileCache 记录: {SegmentID: local_path, S3_key}

读: Scanner → FileCache.get(SegmentID)
   → 命中: 直接读 NVMe (延迟 ~100μs)
   → 未命中:
     1. S3Reader → HTTP Range GET (延迟 ~10-50ms)
     2. 读取的 Segment → 缓存到 FileCache
     3. 返回 Block

淘汰: FileCache 满 → LRU 淘汰最旧的 Segment

关键代码

文件说明
be/src/io/cache/file_cache.hFileCache 接口
be/src/io/fs/s3_file_system.hS3 文件系统抽象
be/src/io/fs/local_file_system.h本地文件系统 (NVMe)

4. TVF (Table Value Function)——查询外部数据文件

使用场景

Cloud 版本中, 数据以文件形式存储在 S3 上。TVF 允许直接在 SQL 中查询 S3 目录中的 Parquet/ORC 文件:

SELECT * FROM TABLE(
    S3(
        "uri" = "s3://bucket/orders/dt=2026-07-*/",
        "format" = "parquet"
    )
) WHERE amount > 100;

实现原理

TVF S3
  → 1. 列出 S3 目录下的文件 (S3 ListObjects)
  → 2. 每个文件 = 一个 Scan Task
  → 3. BE Scanner: S3Reader 读取 Parquet → Block → Pipeline 执行
  → 4. 查询结果返回

与内表的差异: 无 Partition/Bucket/Tablet 的概念, 所有数据都是外部文件, 无统计信息 (CBO 不准确)。


5. Cluster 弹性伸缩

扩缩容

扩容:
  1. 用户/GCS 创建新的 Compute BE 节点
  2. BE 启动 → 连接 MS (注册)
  3. MS 分配 Tablet 到新 BE (均衡)
  4. 新 Tablet 的数据仍在 S3 上 → BE 只需 FileCache 预热

缩容:
  1. 用户/GCS 标记 BE 为 "去活"
  2. BE 上的 Tablet 迁移到其他 BE (只是元数据, 不需数据迁移)
  3. BE 优雅关闭

关键: 因为数据在 S3 上, 扩缩容不涉及数据迁移 (只需元数据更新)。这是 Cloud 版的核心优势。


6. Classic → Cloud 的架构决策变化

维度ClassicCloud变化原因
数据存放本地 NVMe对象存储 (S3)存算分离
Tablet 副本数3 (默认)1 (S3 自带冗余)减少存储成本
数据迁移物理拷贝 Rowset 文件元数据更新 (S3 位置)迁移速度快 100-1000x
Compaction本地 CPU/IO提取到专用 Compaction 节点分离计算和合并任务
查询延迟本地磁盘 ~1msS3 ~10-50ms (首次)但 FileCache 缓存后接近 Classic
扩缩容成本数据迁移 (小时~天)即时 (秒级, 只更新元数据)Cloud 核心优势

7. 常见问题 / 面试题

Q1: Cloud 版如何处理 S3 的一致性保证? A: S3 提供 read-after-write 一致性 (新写入对象立即可读)。Doris Cloud 利用这一保证: (1) 先写 Segment 到 S3 → (2) 验证写入成功 → (3) FE/MS Publish → (4) 查询可见。这与 Classic 的 Publish 一致。

Q2: FileCache 的命中率一般是多少? A: 热数据 (当天数据): 90-95% 缓存命中 (因为活跃查询频繁访问)。历史数据 (>7天): 10-30% 缓存命中。FileCache 的 NVMe 容量决定能缓存多少天数据。

Q3: Cloud vs Classic 的执行引擎是否相同? A: 是的——Pipeline、向量化、优化器全部相同。差异仅在 be/src/io/ 的文件系统层 (S3Reader vs LocalFileReader)。所有上层代码 (Pipeline/Storage) 通过 FileReader 抽象接口访问, 不受底层存储介质影响。

Q4: Meta Service 如何保证元数据的一致性? A: MS 使用与 Classic FE 类似的 Journal/Image 嵌套模型 (也可能是 FoundationDB 等分布式 KV)。MS 的副本通过分布式共识协议 (如 Raft) 保证一致性。


Classic vs Cloud 存算分离架构

下一步

恭喜! 你已经完成了 Doris 的全部 16 个学习文档 (doc-00 到 doc-15)。

下一步建议:

  1. 动手调试: 编译 Doris → 搭建本地 1FE+1BE 集群 → 用 MySQL Client 执行查询 → 断点跟踪调用链
  2. 阅读源码: 从 docs/superpowers/specs/2026-07-08-doris-study-design.md 中的代码路径切入
  3. 参与社区: Apache Doris GitHub Discussion / Slack / 邮件列表