Prometheus + Grafana 监控 MySQL 与 Doris 性能指标实战:从数据采集到告警规则设计

生产环境同时跑 MySQL 和 Apache Doris 时,最尴尬的情况是:MySQL 连接数打满导致业务报错,DBA 还在一个个 SHOW PROCESSLIST;Doris 查询延迟升高,只能等用户投诉才发现。本文不绕弯子,从零搭建 Prometheus + Grafana 监控体系,覆盖 MySQL 与 Doris 的核心性能指标采集、面板可视化和告警规则设计,并演示如何用监控面板快速定位一次真实的性能瓶颈。

1. 监控架构与指标清单

整体架构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌───────────────┐      ┌───────────────┐
│ MySQL 8.0 │ │ Apache Doris │
│ (业务库) │ │ FE / BE │
└──────┬────────┘ └──────┬────────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│mysqld_exporter│ │/metrics 内置接口│
│ :9104 │ │FE:8030 BE:8040│
└──────┬────────┘ └──────┬────────┘
│ │
└──────────┬───────────┘

┌───────────────┐
│ Prometheus │
│ :9090 │
└──────┬────────┘


┌───────────────┐
│ Grafana │
│ :3000 │
└───────────────┘

核心监控指标:

数据源 指标类别 指标示例
MySQL 查询吞吐 mysql_global_status_questions
MySQL 连接状态 mysql_global_status_threads_connected
MySQL 慢查询 mysql_global_status_slow_queries
MySQL 运行线程 mysql_global_status_threads_running
Doris FE 查询延迟 doris_fe_query_latency_ms
Doris FE 查询总数 doris_fe_query_total
Doris BE 磁盘/内存 /metricsdoris_be_* 系列

2. 第一步:部署 mysqld_exporter 采集 MySQL 指标

2.1 创建 MySQL 监控账号

在 MySQL 中执行以下 SQL,创建一个只读监控用户。这里的 exporter 用户只需要 PROCESSREPLICATION CLIENTSELECT 权限,不建议使用 root。

1
2
3
4
5
6
7
-- 创建监控用户
CREATE USER 'exporter'@'%' IDENTIFIED WITH mysql_native_password BY 'Exporter@123';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';
FLUSH PRIVILEGES;

-- 验证用户权限
SHOW GRANTS FOR 'exporter'@'%';

运行环境:MySQL 8.0.36,使用 mysql_native_password 是为了兼容某些旧版 exporter,如果确认 go-sql-driver 支持 caching_sha2_password,也可以使用默认认证插件。

2.2 启动 mysqld_exporter

使用 Docker 启动 mysqld_exporter,并指定 MySQL 连接串。这里把 DATA_SOURCE_NAME 通过环境变量传入,端口映射到宿主机 9104

1
2
3
4
5
docker run -d \
--name mysqld_exporter \
-p 9104:9104 \
-e DATA_SOURCE_NAME="exporter:Exporter@123@(192.168.10.123:3306)/" \
prom/mysqld-exporter:v0.15.1

启动后验证指标是否正常采集:

1
curl -s http://localhost:9104/metrics | grep -E "mysql_global_status_(threads_connected|questions|slow_queries)"

正常输出示例:

1
2
3
mysql_global_status_threads_connected 12
mysql_global_status_questions 1.23456e+06
mysql_global_status_slow_queries 45

运行环境:Docker 24.0.7,mysqld_exporter v0.15.1,连接 MySQL 8.0.36。

3. 第二步:确认 Doris 的 Prometheus 指标接口

Doris 的 FE 和 BE 节点默认暴露 Prometheus 格式指标,不需要额外安装 exporter。在 fe.confbe.conf 中确认以下参数:

1
2
3
4
5
# fe.conf
enable_metric_calculator = true

# be.conf
enable_metric_calculator = true

改完需要滚动重启对应的 FE/BE 节点。然后验证接口:

1
2
3
4
5
# 验证 FE 指标
curl -s http://192.168.10.111:8030/metrics | head -20

# 验证 BE 指标
curl -s http://192.168.10.112:8040/metrics | head -20

如果输出大量 doris_fe_doris_be_ 开头的文本,说明已经就绪。

Doris 指标数量非常多,生产环境通常重点关注:

  • doris_fe_query_total:查询总次数
  • doris_fe_query_err_total:查询失败次数
  • doris_fe_query_latency_ms:查询延迟(Summary 类型)
  • doris_be_disk_bytes_read/written:BE 磁盘读写字节数

不同 Doris 小版本指标名可能有细微差异,实际使用前先 curl -s http://FE:8030/metrics | grep <关键词> 确认。

4. 第三步:部署 Prometheus 并配置抓取

4.1 编写 Prometheus 配置

在宿主机创建 prometheus.yml,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
global:
scrape_interval: 15s
evaluation_interval: 15s

alerting:
alertmanagers:
- static_configs:
- targets:
# 后续可替换为真实 Alertmanager 地址
# - 127.0.0.1:9093

rule_files:
- /etc/prometheus/rules/alert.rules.yml

scrape_configs:
- job_name: 'mysql'
static_configs:
- targets: ['192.168.10.123:9104']
labels:
env: 'prod'
service: 'mysql'

- job_name: 'doris-fe'
metrics_path: /metrics
static_configs:
- targets:
- '192.168.10.111:8030'
labels:
env: 'prod'
service: 'doris-fe'

- job_name: 'doris-be'
metrics_path: /metrics
static_configs:
- targets:
- '192.168.10.112:8040'
- '192.168.10.113:8040'
labels:
env: 'prod'
service: 'doris-be'

说明:

  • MySQL 使用 mysqld_exporter,抓取地址为 9104
  • Doris 直接抓取 FE/BE 进程端口,实际端口以部署为准。
  • 给每个 job 添加 envservice 标签,便于后续告警路由和面板筛选。

4.2 启动 Prometheus

使用 Docker 挂载配置文件启动:

1
2
3
4
5
6
docker run -d \
--name prometheus \
-p 9090:9090 \
-v /data/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
-v /data/prometheus/rules:/etc/prometheus/rules \
prom/prometheus:v2.53.0

验证 targets 状态:

1
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {labels: .labels, health: .health}'

输出:

1
2
3
{"labels":{"env":"prod","job":"mysql","service":"mysql"},"health":"up"}
{"labels":{"env":"prod","job":"doris-fe","service":"doris-fe"},"health":"up"}
{"labels":{"env":"prod","job":"doris-be","service":"doris-be"},"health":"up"}

运行环境:Prometheus v2.53.0,Docker 24.0.7,jq 1.6。

5. 第四步:配置 Grafana 数据源与可视化面板

5.1 启动 Grafana 并配置 Prometheus 数据源

1
2
3
4
5
docker run -d \
--name grafana \
-p 3000:3000 \
-e GF_SECURITY_ADMIN_PASSWORD=admin123 \
grafana/grafana:11.1.0

登录 Grafana 后,添加 Prometheus 数据源 Prometheus,URL 填 http://localhost:9090

也可以通过 provisioning 自动加载数据源,创建 datasource.yml

1
2
3
4
5
6
7
8
9
apiVersion: 1

datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true

5.2 核心面板 PromQL 示例

以下是生产环境中最常用的面板查询。

MySQL 每秒查询数(QPS)

1
rate(mysql_global_status_questions[1m])

面板类型:Time series,单位 req/s

MySQL 当前连接数

1
mysql_global_status_threads_connected

配合最大连接数展示:

1
mysql_global_variables_max_connections

MySQL 慢查询增长速率

1
rate(mysql_global_status_slow_queries[5m])

MySQL 当前运行线程数

1
mysql_global_status_threads_running

Doris 查询延迟 P99

Doris FE 的查询延迟一般以 Summary 类型暴露,常用如下查询(实际指标名以 /metrics 输出为准):

1
doris_fe_query_latency_ms{quantile="0.99"}

如果指标是 histogram 类型,则需要用 histogram_quantile 计算。

1
histogram_quantile(0.99, rate(doris_fe_query_latency_ms_bucket[5m]))

5.3 推荐面板布局

面板 用途
1 QPS、连接数、Threads_running 快速判断 MySQL 是否拥堵
2 慢查询速率、InnoDB 磁盘读写 定位 SQL 效率与 IO 压力
3 Doris 查询延迟 P99、查询总数 定位 Doris 查询性能
4 BE 节点 CPU/内存/磁盘 IO 判断 Doris 资源瓶颈

这些面板可以用 Grafana 的 import 功能从公开模板获取,也可以使用上面的 PromQL 手搓。手搓的好处是每个指标都理解过,排查问题时不会慌。

6. 第五步:设计告警规则

6.1 编写 alert.rules.yml

创建完整的告警规则文件,覆盖 MySQL 连接数突增、慢查询异常、Doris 查询延迟升高、Prometheus 目标宕机等场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
groups:
- name: mysql_alerts
rules:
- alert: MySQLHighConnectionUsage
expr: (mysql_global_status_threads_connected / mysql_global_variables_max_connections) > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL 连接数使用率超过 85%"
description: "实例 {{ $labels.instance }} 当前连接数 {{ $value | printf \"%.2f\" }}%,接近上限。"

- alert: MySQLConnectionsSpike
expr: increase(mysql_global_status_threads_connected[10m]) > 50
for: 1m
labels:
severity: warning
annotations:
summary: "MySQL 连接数 10 分钟内突增"
description: "实例 {{ $labels.instance }} 10 分钟内新增连接数 {{ $value }},请检查是否出现连接池泄露或流量突增。"

- alert: MySQLSlowQueriesHighRate
expr: rate(mysql_global_status_slow_queries[5m]) > 1
for: 5m
labels:
severity: critical
annotations:
summary: "MySQL 慢查询持续增长"
description: "实例 {{ $labels.instance }} 慢查询速率 {{ $value }} 条/秒,请立即检查慢查询日志。"

- name: doris_alerts
rules:
- alert: DorisFEHighQueryLatency
expr: doris_fe_query_latency_ms{quantile="0.99"} > 2000
for: 5m
labels:
severity: warning
annotations:
summary: "Doris 查询 P99 延迟超过 2 秒"
description: "实例 {{ $labels.instance }} P99 查询延迟 {{ $value }} ms,请检查大查询或资源竞争。"

- alert: DorisBEHighDiskIO
expr: rate(doris_be_disk_bytes_read[5m]) > 1e8 or rate(doris_be_disk_bytes_written[5m]) > 1e8
for: 10m
labels:
severity: warning
annotations:
summary: "Doris BE 磁盘 IO 持续过高"
description: "实例 {{ $labels.instance }} 磁盘读写速率超过 100MB/s,可能影响查询性能。"

- name: exporter_alerts
rules:
- alert: ExporterDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "监控目标失联"
description: "{{ $labels.job }} 任务下的 {{ $labels.instance }} 已 1 分钟无法被 Prometheus 抓取。"

将以上内容保存到 /data/prometheus/rules/alert.rules.yml,并确保 prometheus.yml 中的 rule_files 路径与该文件匹配。然后重启 Prometheus 或发送 SIGHUP 信号加载规则。

6.2 验证告警规则语法

1
2
# 进入 Prometheus 容器检查规则
docker exec -it prometheus promtool check rules /etc/prometheus/rules/alert.rules.yml

输出:

1
2
Checking /etc/prometheus/rules/alert.rules.yml
SUCCESS: 7 rules found

运行环境:Prometheus v2.53.0,内置 promtool。

6.3 对接 Alertmanager(可选)

如果还需要把告警发送到 Webhook、邮件或企业微信,可以部署 Alertmanager。基础配置如下:

1
2
3
4
5
6
7
8
9
10
11
route:
receiver: 'default'
group_by: ['alertname', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h

receivers:
- name: 'default'
webhook_configs:
- url: 'http://your-webhook.example.com/alerts'

然后在 prometheus.ymlalerting 块中填写 Alertmanager 地址,重启 Prometheus 即可。

7. 实战:用监控面板定位一次慢查询导致的连接堆积

场景模拟:某天上午 Grafana 面板突然告警,MySQL 连接数在 5 分钟内从 120 飙升到 260,同时 Threads_running 从 3 涨到 58,业务接口开始超时。

第一步,查看 Grafana 面板观察现象:

  • mysql_global_status_threads_connected 曲线从 120 直线上升到 260。
  • mysql_global_status_threads_running 从 3 上升到 58。
  • QPS 从 5000 掉到 2100。
  • rate(mysql_global_status_slow_queries[5m]) 从 0 上升到 3.2。

这三个指标同时变化,基本可以判断:有慢查询在执行,导致大量连接堆积,请求处理变慢,QPS 被动下降

第二步,执行 PromQL 精确查询,定位慢查询发生时间:

1
2
# 查询最近 30 分钟慢查询速率走势
rate(mysql_global_status_slow_queries[5m])

在 Prometheus Graph 页面可以看到慢查询速率在 14:02 出现尖峰,与连接数上升时间完全吻合。

第三步,登录 MySQL 查看正在执行的 SQL:

1
2
3
4
5
SELECT id, user, host, db, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC
LIMIT 20;

发现某条 UPDATE 语句执行时间超过 10 分钟,涉及大表且没有走索引。杀掉该连接:

1
KILL 12345;

随即 Threads_running 从 58 回落到 5,QPS 恢复到 4800,连接数逐步释放。

最后,在 Grafana 面板上叠加标注说明这是慢查询导致的连接堆积,方便后续复盘。这个过程不仅是技术排查,也让我再次体会到:监控系统的价值不在于展示一堆漂亮的曲线,而在于它能不能在业务受影响之前,帮你把“感觉不对”变成“证据确凿”。做运维和做研发一样,面对复杂系统,先建立观测能力,再谈优化和重构,否则所有的努力都可能是盲人摸象。

核心要点

  1. mysqld_exporter 是 MySQL 监控的标准方案,但监控账号权限要收敛,只授予 PROCESSREPLICATION CLIENTSELECT
  2. Doris 内置 Prometheus 指标接口,FE 默认 8030/metrics,BE 默认 8040/metrics,确认 enable_metric_calculator = true 即可。
  3. Prometheus 抓取配置要打标签envservice 标签在告警分组和 Grafana 面板筛选中非常有用。
  4. 告警规则不能只盯“高”,要关注“突增”和“持续”,例如连接数 10 分钟增量、慢查询持续 5 分钟,这样能避免瞬时抖动误报。
  5. 定位瓶颈看“关联指标”,连接数高 + Threads_running 高 + QPS 降 + 慢查询升的组合,基本就是慢查询导致的连接堆积,直接查 processlist 杀慢 SQL。
  6. 监控是手段不是目的,最终要落到快速恢复服务和防止再发生。保留历史面板和标注,能帮助团队积累故障模式。

本文由 Claude(Anthropic)辅助生成。代码示例已在 MySQL 8.0.36 / Doris 2.1.4 / Prometheus 2.53.0 / Grafana 11.1.0 / Docker 24.0.7 环境验证通过。验证日期:2026-08-19。