Doris 实战与架构

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

资源管理与 Workload Group

从 Workload Group、MemTracker 和 RuntimeFilter 理解资源隔离、内存控制与查询治理。

阅读时间
40 分钟 | **前置阅读**: [doc-04](doc-04-pipeline-execution.md) (Pipeline)
学习路径
Doris 实战与架构
内容来源
Doris 深度笔记

预计阅读时间: 40 分钟 | 前置阅读: doc-04 (Pipeline) 下一次阅读: doc-15 (Cloud)


1. Workload Group 模型

资源隔离机制

Workload Group (FE 定义)
├── Group "etl"       (CPU: 30%, Mem: 40%, Concurrency: 5)
├── Group "adhoc"     (CPU: 30%, Mem: 20%, Concurrency: 10)
├── Group "dashboard" (CPU: 40%, Mem: 40%, Concurrency: 20)
└── Group "default"   (无限制)

每个查询标记属 Group → BE TaskScheduler 按 Group 配额调度

Cgroup 集成

BE 可以使用 Linux Cgroup 的 CPU 子系统和 Memory 子系统来实现硬隔离:

Group "etl" → /sys/fs/cgroup/cpu/doris/etl/
  → cpu.cfs_quota_us = 100000  (100 ms/100ms = 1 个 CPU)
  → 查询的 Pipeline Workers 绑定到这个 cgroup
  → 即使有大量 CPU 空闲, "etl" Group 也只能用到 1 个 CPU

源码导航

文件关键类/方法职责
be/src/runtime/memory/mem_tracker.hMemTracker, consume(), release()内存跟踪
be/src/runtime/memory/mem_pool.hMemPool内存池(小块分配)
be/src/exec/runtime_filter/runtime_filter_mgr.hRuntimeFilterMgrRuntimeFilter 管理
be/src/runtime/runtime_filter.hIRuntimeFilterRuntimeFilter 基类
be/src/runtime/bloom_filter.hBloomFilter (RF)BloomFilter RuntimeFilter
be/src/exec/pipeline/task_scheduler.hTaskSchedulerPipeline 调度(CPU 隔离)

2. 内存管理——MemTracker → MemPool → OOM 保护

资源管理与 Workload Group 图 01

内存层级

全局 MemTracker (BE 进程级)
├── Query MemTracker (查询级)
│   ├── Fragment MemTracker
│   │   └── Operator MemTracker (Pipeline 算子的内存)
│   └── ExprContext MemTracker (表达式计算的临时内存)
├── Load MemTracker (导入级)
│   └── DeltaWriter MemTracker
├── Compaction MemTracker
└── Cache MemTracker (FileCache, LookupCache)

内存限制与 OOM 保护

MemTracker::consume(bytes)
  → 1. 检查: 当前 tracker 的 used + bytes < limit?
  → 2. 是 → 分配, used += bytes
  → 3. 否 → 尝试释放缓存 (FileCache/LookupCache)
  → 4. 仍超限 → OOM 保护:
      - Query 超限: cancel 查询 (释放其所有内存)
      - Load 超限: 暂停写入, 触发反压
      - 进程超限: 杀死最大的查询 (BigQuery OOM)

关键源码

文件说明
be/src/runtime/memory/mem_tracker.hMemTracker 定义
be/src/runtime/memory/mem_pool.h内存池 (小块分配)
be/src/runtime/memory/thread_mem_tracker_mgr.h线程级内存跟踪

3. Query Queue——并发控制

队列语义

查询到达 BE:
  → 1. 查询标记的 Workload Group 可接受新查询?
      → 并发数 < Group.max_concurrency → 接受, 队列长度+1
      → 并发数 ≥ Group.max_concurrency → 进入等待队列
  → 2. 等待超时? (query_timeout)
      → 超时→取消查询 + 返回错误
      → 未超时→轮询等待 (1s 间隔)
  → 3. 前面的查询完成 → 队列前进 → 出队开始执行

4. RuntimeFilter——内存与执行效率的权衡

原理

查询: SELECT * FROM t1 JOIN t2 ON t1.id = t2.id
       WHERE t1.dt = '2026-07'

RuntimeFilter 生成 (Join Build 端):
  → t1 的子查询执行后, 在 id 列上创建 BloomFilter
  → BloomFilter 作为 "RuntimeFilter" 推送到 t2 的 Scan 端
  → t2 Scanner 使用 BloomFilter 预过滤 (类似索引)
  → 大幅减少 t2 的扫描量

内存权衡

RF BloomFilter 大小 ∝ t1.id 的 distinct count
  几千 distinct → RF 几 KB
  百万 distinct → RF 几 MB
  亿 distinct → RF 数百 MB

配置: runtime_filter_max_size (默认 16MB)
  → RF > 16MB → 截断 (丢弃部分 hash) → 精确度下降
  → 优化: 为高基数列创建 Bitmap RF (而非 BloomFilter)

源码

文件说明
be/src/runtime/runtime_filter_mgr.hRF 管理
be/src/runtime/runtime_filter.hRF 基类
be/src/runtime/bloom_filter.hBloomFilter RF 实现

5. 常见问题 / 面试题

Q1: Workload Group 的 CPU 隔离如何实现公平调度? A: BE TaskScheduler 使用加权公平队列 (WFQ): Group 的 CPU 配额 = CPU_total × (group_weight / sum_of_all_weights)。调度器优先选择 CPU 配额未用完的 Group 中的 Task。如果所有 Group 用完配额 → 按 Fair Share 轮转。

Q2: MemTracker 的 limit 检查对性能的影响? A: 几乎无——仅消耗一次原子操作 (atomic add)。MemTracker 使用 std::atomic<int64_t>, consume() 中的 check 只是 if (used > limit) 比较, CPU 开销可忽略。

Q3: RuntimeFilter 在什么情况下不值得开启? A: (1) 小表 Join 小表 (filter 生成成本 > 过滤收益); (2) RF 列基数极高 (BloomFilter 过大, 查询被截断后的过滤效果差); (3) 查询的总执行时间 <100ms (RF 生成延迟不被摊销)。


Workload Group / MemTracker / RuntimeFilter

下一步