预计阅读时间: 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.h | MemTracker, consume(), release() | 内存跟踪 |
be/src/runtime/memory/mem_pool.h | MemPool | 内存池(小块分配) |
be/src/exec/runtime_filter/runtime_filter_mgr.h | RuntimeFilterMgr | RuntimeFilter 管理 |
be/src/runtime/runtime_filter.h | IRuntimeFilter | RuntimeFilter 基类 |
be/src/runtime/bloom_filter.h | BloomFilter (RF) | BloomFilter RuntimeFilter |
be/src/exec/pipeline/task_scheduler.h | TaskScheduler | Pipeline 调度(CPU 隔离) |
2. 内存管理——MemTracker → MemPool → OOM 保护

内存层级
全局 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.h | MemTracker 定义 |
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.h | RF 管理 |
be/src/runtime/runtime_filter.h | RF 基类 |
be/src/runtime/bloom_filter.h | BloomFilter 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 生成延迟不被摊销)。

下一步
- Cloud: doc-15-cloud-architecture.md——存算分离版本的架构变化