Doris 实战与架构

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

Catalog 元数据管理与 Journal

从 Catalog 层级和 Journal 日志理解 Doris 元数据的组织、持久化与恢复。

阅读时间
45 分钟 | **前置阅读**: [doc-00](doc-00-architecture-overview.md) §2 (FE拓扑), [doc-06](doc-06-storage-tablet-rowset-segment.md) (Tablet)
学习路径
Doris 实战与架构
内容来源
Doris 深度笔记

预计阅读时间: 45 分钟 | 前置阅读: doc-00 §2 (FE拓扑), doc-06 (Tablet) 下一次阅读: doc-11 (FE HA)


1. 元数据模型总览

Catalog 元数据管理与 Journal 图 01

层级结构

Catalog (fe/fe-core/.../catalog/)
├── InternalCatalog (内表)
│   └── Database
│       └── OlapTable
│           ├── Partition 0
│           │   ├── Index (Rollup/MV) 0
│           │   │   ├── Tablet 0 → Replica(BE-1), Replica(BE-2), Replica(BE-3)
│           │   │   ├── Tablet 1 → ...
│           │   │   └── Tablet N
│           │   └── Index 1 (物化视图)
│           ├── Partition 1 → ...
│           └── ...
├── HmsExternalCatalog (Hive Metastore)
├── IcebergExternalCatalog
├── HudiExternalCatalog
├── EsExternalCatalog (Elasticsearch)
└── JdbcExternalCatalog (MySQL/PostgreSQL)

关键类

层级源码位置职责
CatalogEnvfe/fe-core/.../catalog/Env.java全局元数据容器(单例)
DBDatabasefe/fe-core/.../catalog/Database.java库级别元数据 + 事务管理
TableOlapTablefe/fe-core/.../catalog/OlapTable.java表:分区/副本/索引/属性
PartitionPartitionfe/fe-core/.../catalog/Partition.java分区: Tablets 集合
TabletTablet (FE)fe/fe-core/.../catalog/Tablet.javaFE 侧 Tablet 元数据
ReplicaReplicafe/fe-core/.../catalog/Replica.java副本: BE 位置/状态

源码导航

文件关键类/方法职责
fe/fe-core/.../catalog/Env.javaEnv (单例)全局元数据容器
fe/fe-core/.../catalog/Database.javaDatabase库级元数据(Txn管理)
fe/fe-core/.../catalog/OlapTable.javaOlapTable表:分区/副本/物化视图
fe/fe-core/.../catalog/Partition.javaPartition分区:Tablets 集合
fe/fe-core/.../catalog/Tablet.javaTablet (FE)FE 侧 Tablet 元数据
fe/fe-core/.../catalog/Replica.javaReplica副本:BE 位置/状态
fe/fe-core/.../persist/EditLog.javaEditLog, logJsonEdit()Journal 写入 (persist 包)
fe/fe-core/.../journal/bdbje/BDBJEJournal.javaBDBJEJournalBDBJE 实现 (journal 包)

2. Catalog 核心代码路径

初始化

FE 启动 → Env 初始化
  → 1. 回放 Journal (恢复上次 Checkpoint 后的操作)
  → 2. 加载 Image (最新的元数据快照)
  → 3. 回放增量 Journal → 恢复到最新状态
  → 4. 打开 BDBJE Replicated Environment
  → 5. 选举 Master/Follower (BDBJE)

DDL 执行路径

CREATE TABLE t (id BIGINT, name STRING)
  → StmtExecutor.execute()
    → CreateTableStmt
      → Env.createTable(db, tableName, ...)
        → 1. 写入 EditLog (Journal)   ← 持久化 (故障恢复)
        → 2. 更新 FE 内存: Catalog.addTable()
        → 3. 通知 BE: CreateTabletTask  ← BE 创建 Tablet 目录
        → 4. 写入 BDBJE (复制到 Follower)

3. EditLog 与 Journal 格式

日志类型

日志类型操作示例
OperationType.OP_CREATE_DB创建数据库CREATE DATABASE
OperationType.OP_CREATE_TABLE创建表CREATE TABLE
OperationType.OP_ADD_PARTITION添加分区ALTER TABLE ADD PARTITION
OperationType.OP_FINISH_CONSISTENCY_CHECKTablet 修复完成Clone 完成
OperationType.OP_ALTER_JOB_V2Schema ChangeALTER TABLE MODIFY COLUMN

Journal 持久化流程

编辑操作 → EditLog.logEdit(OperationType, Writable)
  → 1. 序列化为 byte[]
  → 2. BDBJE.put(key=logId, value=bytes)  ← 同步写入 BDBJE
  → 3. BDBJE 复制: Master → Follower(s) (自动复制)
  → 4. Follower 回放: apply(OperationType, bytes) → 更新本地 Catalog

源码定位

文件用途
fe/fe-core/.../journal/Journal.javaJournal 接口
fe/fe-core/.../journal/bdbje/BDBJEJournal.javaBDBJE 实现
fe/fe-core/.../journal/EditLog.javaEditLog 写入入口

4. 元数据持久化——Image + Journal

两层存储模型

Image:       元数据全量快照 (类似 Raft Snapshot)
             每隔 N 个 Journal Entry 生成一次
             所有 FE (含 Follower) 本地都有完整 Image

Journal:     增量变更记录
             自上次 Image 后的所有修改操作
             格式: {logId: OperationType + Payload}

启动恢复流程

FE 崩溃恢复:
  1. 读取本地最新 Image → 加载到内存 (~分钟级)
  2. 读取 Journal 自 Image.logId 后的增量 Entry → 回放 (~秒级)
  3. Catalog 恢复到崩溃前的最新状态
  4. 加入 BDBJE 集群 → 追赶 Master 的最新 Journal Entry
  5. FE 变为 Follower → 可接受查询

5. 元数据 GC / Recycle Bin

删除表/分区 的多阶段

Drop Table t:
  1. Logical Delete: Catalog 中移除 t → Table 进入 RecycleBin
     → 可见性: 用户看不到 t
     → RecycleBin: 保留 N 分钟 (默认 1天, `catalog_trash_expire_second`)
  2. Physical Delete: RecycleBin 到期 → 清理所有 Tablet 和 Replica
     → FE → BE: DropTabletTask → BE 删除磁盘上的 Rowset 文件

6. External Catalog——联邦查询入口

架构

用户: SELECT * FROM hive_catalog.db.orders WHERE dt='2026-07';
                 │
FE Nereids  ──► HmsExternalCatalog
                 │  1. 连接 Hive Metastore
                 │  2. 获取 orders 表的 Schema + 文件位置 (HDFS/S3)
                 │  3. 生成 PhysicalFileScan (读 Parquet/ORC 文件)
                 │
BE Pipeline  ──► FileReader (HdfsReader / S3Reader)
                  直接读外部文件的 Parquet/ORC → 解压 → Block

支持的 External Catalog

Catalog存储位置数据格式
HMS (Hive)HDFS / S3Parquet, ORC, Text
IcebergHDFS / S3 / GCSParquet, ORC, Avro
HudiHDFS / S3Parquet (MoR/CoW)
ElasticsearchES ClusterJSON (索引)
JDBCMySQL/PG/OracleTable (通过 JDBC 查询)

7. 常见问题 / 面试题

Q1: BDBJE 为什么比 Raft 实现更合适? A: BDBJE 的 Journal 是"嵌入式 Java KV 存储 + 自带复制", 与 FE 代码深度集成(序列化/回放在同进程内), 比单独的 Raft 库(需额外的网络层+存储引擎)简单。代价: BDBJE 不是广为人知的组件, 社区维护压力大。

Q2: Image 文件过大会有什么问题? A: (1) FE 启动时加载 Image 慢; (2) 内存占用大 (所有元数据在 JVM heap); (3) Checkpoint 耗时增加。Doris 的生产建议: Tablet 数量控制在 100 万以内, 单 FE 堆内存 32-64GB。

Q3: 外部 Catalog 如何维护统计信息? A: FE 定期 (可配) 扫描外部表的文件元数据 (HMS 的 TableStats), 或触发计算任务采样。外部表的统计信息通常比内表粗糙 (只有 Row Count + File Size, 无 Histogram), 影响 CBO 准确度。


元数据层级: Catalog → Tablet → Replica

下一步