预计阅读时间: 60 分钟 前置阅读: 全部 doc (运维必备,需要了解所有子系统) 下一次阅读: doc-17(表设计与建模), doc-18(慢 SQL 与 Profile)
1. Metrics 体系
源码导航
| 文件 | 关键类/方法 | 职责 |
|---|---|---|
fe/fe-core/.../metric/MetricRepo.java | MetricRepo (单例) | FE 指标注册中心 |
fe/fe-core/.../metric/CounterMetric.java | CounterMetric | Counter 类型 |
fe/fe-core/.../metric/GaugeMetricImpl.java | GaugeMetricImpl<T> | Gauge 类型 |
be/src/common/metrics/doris_metrics.h | DorisMetrics | BE 指标注册中心 |
be/src/common/metrics/metrics.cpp | MetricsVisitor | 指标收集与输出 |
be/src/service/http/action/metrics_action.cpp | MetricsAction | HTTP /metrics 端点 |
Metric 类型
| 类型 | FE 类 | 说明 | 典型指标 |
|---|---|---|---|
| Counter | CounterMetric | 单调递增计数 | query_total, load_bytes_total |
| Gauge | GaugeMetricImpl<T> | 瞬时值(可增可减) | tablet_num, memory_used |
| Histogram | 基于统计 | 分桶统计 | query_latency, journal_write_latency |
BE 端点
http://<be_host>:8040/metrics → Prometheus 格式
http://<fe_host>:8030/metrics → FE 指标
2. 关键 Metrics 速查
FE 指标
| Metric | 类型 | 含义 | 异常阈值 |
|---|---|---|---|
query_total | Counter | 累计查询数 | — |
query_err | Counter | 失败查询数 | > 1% 总查询 |
fe_journal_write_latency_ms | Histogram | Journal 写入延迟 | P99 > 100ms |
edit_log_size_bytes | Counter | EditLog 累计大小 | — |
tablet_report_latency_ms | Histogram | Tablet 心跳处理延迟 | P99 > 500ms |
fe_memory_used_bytes | Gauge | FE JVM Heap 使用量 | > 80% MaxHeap |
request_total | Counter | HTTP/MySQL 请求总数 | — |
BE 指标
| Metric | 类型 | 含义 | 异常阈值 |
|---|---|---|---|
tablet_num | Gauge | Tablet 总数 | — |
compaction_deltas_total | Counter | Compaction 合并 Delta 数 | — |
memory_pool_bytes_used | Gauge | BE 内存使用量 | > 80% Total |
segment_read_latency_ms | Histogram | Segment 读取延迟 | P99 > 100ms |
fragment_requests_total | Counter | Fragment 请求数 | — |
brpc_endpoint_latency | Histogram | Brpc 点对点延迟 | P99 > 50ms |
load_bytes_total | Counter | 累计导入字节数 | — |
3. Profile——查询执行分析
架构
Profile 是 Doris 最重要的性能诊断工具。每次查询都生成一个 RuntimeProfile 树,包含每个算子(Fragment、Pipeline、Operator)的执行统计。
源码导航
| 文件 | 关键类/方法 | 职责 |
|---|---|---|
fe/fe-core/.../profile/RuntimeProfile.java | RuntimeProfile | Profile 节点(树形结构) |
fe/fe-core/.../profile/ExecutionProfile.java | ExecutionProfile | 查询级 Profile |
fe/fe-core/.../profile/SummaryProfile.java | SummaryProfile | 汇总信息 |
be/src/runtime/runtime_profile.h | RuntimeProfile (BE) | BE 端 Profile 计数器 |
fe/fe-core/.../qe/ProfileManager.java | ProfileManager | Profile 管理/缓存 |
Profile 如何使用
-- 查看最近一个查询的 Profile
SHOW PROFILE;
-- 查看特定查询的 Profile (需要 query_id)
SHOW PROFILE "/" FROM <query_id>;
-- 或通过 HTTP API
curl http://fe:8030/api/profile?query_id=<query_id>
Profile 树结构解读
Execution Profile <query_id>
├── Summary
│ ├── Query Type: SELECT
│ ├── Start Time: 2026-07-08 10:00:00
│ ├── End Time: 2026-07-08 10:00:02
│ ├── Total Time: 2.3s
│ └── Query State: FINISHED
│
├── Fragment 0 (Root)
│ ├── Pipeline 0
│ │ ├── ExchangeSourceOperator
│ │ │ ├── RowsReturned: 1,000,000
│ │ │ ├── BlocksRead: 245
│ │ │ └── TotalTime: 200ms
│ │ └── AggSinkOperator
│ │ ├── RowsProcessed: 1,000,000
│ │ └── TotalTime: 500ms
│ └── ...
│
├── Fragment 1 (Leaf)
│ ├── Pipeline 0
│ │ ├── OlapScanOperator
│ │ │ ├── RowsReturned: 5,000,000
│ │ │ ├── TabletsScanned: 120
│ │ │ ├── SegmentReadTime: 800ms
│ │ │ └── IndexFilterTime: 50ms
│ │ └── FilterOperator
│ │ ├── RowsFiltered: 4,000,000
│ │ └── TotalTime: 100ms
│ └── ...
关键 Profile 指标
| 指标 | 含义 | 优化方向 |
|---|---|---|
RowsReturned / RowsFiltered | 扫描行与过滤行比例 | 过滤率低 → 索引不够/谓词不合理 |
SegmentReadTime | Segment 读取耗时 | 太高 → 可能缺少索引过滤或 Compaction 不及时 |
IndexFilterTime | 索引过滤耗时 | 太低 → 索引没有生效 |
ExchangeTime | Shuffle 耗时 | 太高 → 可能数据倾斜或网络瓶颈 |
TotalTime | 算子总耗时 | 对比各算子找瓶颈 |
4. Audit Log——查询审计
架构
每一条 SQL (查询/DDL/DML) 都会记录到 Audit Log 中,包含执行时间、扫描行数、返回行数、状态等。
源码导航
| 文件 | 关键类/方法 | 职责 |
|---|---|---|
fe/fe-core/.../qe/AuditLogHelper.java | AuditLogHelper, AuditEvent | 审计日志生成 |
fe/fe-core/.../plugin/AuditEvent.java | AuditEvent, EventType | 审计事件定义 |
日志格式
# fe/log/fe.audit.log
2026-07-08 10:00:00,123|query_id|user|db|SELECT ...|2000(ms)|120(tablets)|5000000(scanRows)|1000000(returnRows)|OK
5. 排障场景速查表
场景 1: 查询变慢——如何定位瓶颈
症状: 某个查询突然变慢 (从 1s → 10s)
排查路径:
1. SHOW PROFILE → 找到 TotalTime 最大的 Fragment/Operator
2. 检查 OlapScanOperator.RowsReturned vs RowsFiltered 比例
→ RowsReturned 远大于正常值 → 分区裁剪/索引过滤失效
→ RowsReturned 正常但 SegmentReadTime 高 → Compaction 问题 (Rowset 太多)
3. 检查 ExchangeTime → Shuffle 是否成为瓶颈
4. 检查 AggregationTime → 聚合是否在 BE 还是 FE 端 (fe 端聚合 = 慢)
5. 检查 `ANALYZE TABLE` → 统计信息是否过期
场景 2: Compaction 跟不上写入
症状: 查询越来越慢, Profile 中 OlapScanOperator.TabletsScanned 持续增长
原因: Rowset Accumulation — 写入速度 > Cumulative Compaction 速度
排查路径:
1. BE metrics: compaction_deltas_total (Compaction 合并 Delta 总数)
2. show tablet <tablet_id> → Rowset 数量是否 > 20
3. show proc "/cluster_balance" → 是否有 Compaction 积压
4. 检查 BE CPU / IO 使用率 (Compaction 占用)
解决:
1. 增大 max_cumulative_compaction_num_singleton_deltas (合并更多 Delta)
2. 减少并发导入量
3. 触发手动 Base Compaction
场景 3: FE 内存 OOM
症状: FE Full GC 频繁 / OOM
原因: 元数据过多 (Tablet 数超标) 或 Journal 积压
排查路径:
1. FE metrics: fe_memory_used_bytes > 80% MaxHeap
2. show proc "/statistic" → Tablet 总数 (建议 <100万)
3. show proc "/cluster_balance" → Clone/Repair 任务积压
4. FE log → "java.lang.OutOfMemoryError"
解决:
1. 增大 FE JVM Heap (-Xmx)
2. 清理不用的旧表/Database
3. 减少 Tablet 数量 (增大 Bucket 大小)
4. 减少 Journal 保留时间
场景 4: BE 磁盘满
症状: BE 磁盘使用率 > 90%, 写入被拒绝
排查路径:
1. BE metrics: disk_total / disk_used
2. show proc "/backends" → DiskUsedPercent
3. BE 本地: du -sh /data/doris/data/*
解决:
1. 触发垃圾回收 (GC 线程): 清理旧 Rowset, 清理 Trash
2. 手动删除不再需要的 Partition/Table (先 DROP TABLE, 等待 GC)
3. 触发 Base Compaction (合并 Rowset 释放空间)
4. 扩容 BE / 加磁盘
场景 5: Publish 超时
症状: 数据导入成功, 但查询不可见 (延迟 >30s)
原因: PublishVersionTask 超时/积压
排查路径:
1. FE metrics: publish_version_latency_ms
2. show proc "/transactions/<db_id>" → 查看 PENDING 状态事务
3. BE log: "PublishVersionTask timeout"
4. 检查 BE 心跳是否正常 (TabletReport)
解决:
1. 增大 publish_version_task_timeout_s
2. 减少并发导入量
3. 检查网络延迟 (FE→BE RPC)
场景 6: 数据倾斜
症状: 某些 BE 的 CPU/IO 远高于其他 BE
原因: 数据倾斜 — 某些 Tablet 过大或访问频率过高
排查路径:
1. show proc "/statistic" → Tablet 大小分布 (min vs max)
2. show proc "/tablets_distribution" → 按 BE 分布的 Tablet 数
3. Profile: 某 SubTask 的 RowsScanned 远高于其他 Subtask
解决:
1. 重新选择分桶键 (避免热点 Key)
2. 增加 Bucket 数 (更均匀分布)
3. 手动 Clone 热点 Tablet 到其他 BE
场景 7: Brpc 通信故障
症状: BE 间数据传输超时 / Fragment 执行失败
原因: Brpc Channel 超时 / 连接断开
排查路径:
1. BE log: "backup request timeout" / "Channel timeout"
2. BE metrics: brpc_endpoint_latency (P99)
3. 网络: ping / netstat 检查 BE 间连通性
解决:
1. 增大 rpc_timeout_ms
2. 检查网络带宽 (Shuffle 阶段的网络使用)
3. 启用 Backup Request (降低 P99 延迟)
场景 8: Memory Exceed Limit
症状: 查询被 Cancel: "Memory exceed limit"
原因: 查询内存使用超过 query_mem_limit (或 Workload Group 的 mem_limit)
排查路径:
1. FE Audit Log: "Memory exceed limit" 错误
2. Profile: 各算子的 PeakMemoryUsage
3. BE metrics: memory_pool_bytes_used
解决:
1. 增大 query_mem_limit / Workload Group mem_limit
2. 减少并发查询数
3. 优化查询: 减少 JOIN 大小, 增加过滤条件
4. 使用 Broadcast Join (小表) 替代 Shuffle Join

6. 监控栈推荐
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Doris │───►│ Prometheus │───►│ Grafana │
│ /metrics │ │ (Pull/Push) │ │ (Dashboard) │
│ (FE + BE) │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐ ┌──────────────┐
│ Audit Log │───►│ ELK (可选) │
│ (fe.audit.log)│ │ 日志聚合+告警 │
└──────────────┘ └──────────────┘
Prometheus 配置示例
# prometheus.yml
scrape_configs:
- job_name: 'doris-fe'
static_configs:
- targets: ['fe1:8030', 'fe2:8030', 'fe3:8030']
- job_name: 'doris-be'
static_configs:
- targets: ['be1:8040', 'be2:8040', 'be3:8040']
Grafana Dashboard 建议面板
| 面板 | 数据源 | 关键报警 |
|---|---|---|
| 查询 QPS & 延迟 | FE metrics | P99 > 10s |
| FE JVM Heap | FE metrics | > 80% alert |
| BE 内存 / 磁盘 | BE metrics | Disk > 85%, Mem > 80% |
| Compaction 积压 | BE metrics | Pending compaction > 100 |
| Publish 延迟 | FE/BE metrics | P99 > 30s |
| Tablet 总数 | FE metrics | > 1,000,000 alert |
| 查询错误率 | FE audit | Error rate > 1% |
7. 常见问题 / 面试题
Q1: Profile 中的 RowsReturned 比 RowsFiltered 大很多,说明什么? A: 说明索引过滤效果不好——大量数据从存储层读取但在上层被过滤。浪费了磁盘 I/O 和网络传输。需要检查: 谓词是否有合适的索引 (BloomFilter/Bitmap),ZoneMap 是否生效,分区裁剪是否生效。
Q2: 为什么 Show Tablet 看到 Rowset 数量持续增长? A: Cumulative Compaction 跟不上写入速度。每个写入产生一个 Delta Rowset,Compaction 负责合并。如果 Cumulative Compaction 频率和速度不够,Rowset 数量会增长。极端情况下(Rowset Runaway)会导致查询性能严重退化。
Q3: FE OOM 的主要原因有哪些? A: (1) Tablet 数量过多(建议 < 100万),每个 Tablet 在 FE 内存中有元数据条目; (2) Journal 积压(Checkpoint 间隔过长),FE 内存持有太多未 Checkpoint 的 Journal Entry; (3) 查询结果缓存过大(SQL Cache)。解决: 清理历史表/分区,减少 Tablet 数量,调整 Checkpoint 参数。
Q4: BE 磁盘写满后数据会丢失吗? A: 不会丢失,但写入会被拒绝。Doris 有磁盘保护机制: (1) 磁盘使用率 > 90% → 新 Tablet 不再分配给该 BE; (2) 磁盘使用率 > 95% → 拒绝写入请求。已有数据不受影响。需要及时扩容或清理。
Q5: Doris 的监控和排障中最容易被忽略的指标是什么?
A: fe_journal_write_latency_ms(FE Journal 写入延迟)。这个指标异常高会导致: DDL 变慢 → FE 间复制变慢 → Follower 落后 → 查询返回旧数据。但这个指标不在常见监控面板中,经常到出问题时才发现。
下一步
恭喜! 你已经完成了 Doris 的全部 17 个学习文档 (doc-00 到 doc-16)。
下一步建议:
- 动手调试: 编译 Doris → 搭建本地 1FE+1BE 集群 → 用 MySQL Client 执行查询 → 断点跟踪调用链
- 阅读源码: 从架构图中的代码路径切入
- 参与社区: Apache Doris GitHub Discussion / Slack / 邮件列表
- 配置监控: Prometheus + Grafana 搭建本地监控