12 KiB
tags, create time
| tags | create time | ||||||||
|---|---|---|---|---|---|---|---|---|---|
|
2026-05-16 00:00 |
监控指标
概述
有效的监控体系能让你在问题爆发前发现问题。MySQL 的监控涵盖 性能、可用性、容量 和 安全 四个维度。
[!TIP] 核心思想 监控的目的不是「收集更多数据」,而是 「用最少的关键指标发现最多的问题」。初学者常犯的错误是把所有 SHOW STATUS 变量都记录下来——但真正需要告警的可能不到 20 个。先定义你的 SLA/SLO,再决定监控什么。
一个典型的 MySQL 监控栈自下而上:
graph TD
A["MySQL Server"] -->|"SHOW GLOBAL STATUS / performance_schema"| B["mysqld_exporter"]
B -->|"HTTP scrape"| C["Prometheus"]
C -->|"查询 + 告警规则"| D["Grafana"]
C -->|"Alertmanager"| E["告警通道\n钉钉/飞书/PagerDuty"]
D -->|"Dashboard"| F["运维 & 开发"]
style A fill:#f9d,stroke:#333
style C fill:#bee,stroke:#333
style D fill:#ddf,stroke:#333
style E fill:#fdb,stroke:#333
理解了这个架构,你就能定位任何一个环节出问题时该检查哪里。
核心指标速查
吞吐量类
| 指标 | SHOW STATUS 名称 | 健康范围 | 说明 |
|---|---|---|---|
| QPS | Queries |
视硬件而定 | 包含内部重写(如 INSERT → multiple row),非客户端请求数 |
| TPS | Com_commit + Com_rollback |
视硬件而定 | 只统计 InnoDB 事务型操作 |
| Threads Connected | Threads_connected |
< MaxConnections × 0.8 | 当前已建立的连接总数 |
| Threads Running | Threads_running |
< CPU 核心数 | 正在执行查询的线程数(瞬时高峰可短暂超限) |
[!WARNING] QPS ≠ 客户端请求数
Queries计数的是服务器内部处理的查询数,而非客户端发送的请求数。一条INSERT INTO t VALUES(1),(2),(3)会被计数为 1 次 Client Query,但可能被优化器展开后计为多次 Internal Queries。如果需要精确的客户端请求量,应看Com_select+Com_insert+Com_update+Com_delete的总和。
-- 计算实时 QPS 和 TPS
SHOW GLOBAL STATUS LIKE 'Queries'; -- 累计查询数
SHOW GLOBAL STATUS LIKE 'Com_commit'; -- 累计 commit 数
SHOW GLOBAL STATUS LIKE 'Com_rollback'; -- 累计 rollback 数
-- QPS = Queries / Uptime
-- TPS = (Com_commit + Com_rollback) / Uptime
// Go 中采集指标的推荐方式:直接查询并返回 map
func collectStatus(db *sql.DB) (map[string]string, error) {
rows, err := db.Query("SHOW GLOBAL STATUS")
if err != nil {
return nil, err
}
defer rows.Close()
metrics := make(map[string]string)
for rows.Next() {
var name, value string
rows.Scan(&name, &value) // value 可能是数字或字符串,统一读取为 string
metrics[name] = value
}
return metrics, rows.Err()
}
这段代码的核心思路是 一次查询拿全部指标,避免 N+1 次网络往返。实际生产环境中,你会在每个指标之间做差值运算得到速率,详见下方公式推导。
速率计算公式:
ΔQPS = (Queries_now - Queries_prev) / (Timestamp_now - Timestamp_prev)
ΔTPS = ((Com_commit_now + Com_rollback_now) - (Com_commit_prev + Com_rollback_prev)) / ΔSeconds
Prometheus 的 mysqld_exporter 会自动帮你完成这个差值计算,如果用原生 SQL 采样则需自行实现。
InnoDB 引擎指标
InnoDB 是绝大多数 MySQL 业务场景使用的存储引擎,其内部指标决定了数据库的整体健康状况。
| 指标 | SHOW STATUS 名称 | 健康阈值 | 说明 |
|---|---|---|---|
| Buffer Pool 读请求 | Innodb_buffer_pool_read_requests |
— | 从 Buffer Pool 中读取的逻辑请求总数 |
| Buffer Pool 磁盘读取 | Innodb_buffer_pool_reads |
命中率 ≥ 99% | 需回磁盘读取的次数(未命中) |
| Redo Log 写入量 | Innodb_data_written |
突增意味着写入密集 | 累计写入 Redo Log 的数据字节数 |
| Redo Log flush 等待 | Innodb_log_waits |
应接近 0 | Redo Log 缓冲区满导致刷盘等待 |
| 死锁次数 | Innodb_deadlocks |
不应持续增加 | 每次死锁回滚一个事务 |
| 平均行锁等待 | Innodb_row_lock_time_avg |
< 10ms | 行级锁平均等待时间 |
| 行锁总等待时间 | Innodb_row_lock_time |
监控趋势 | 所有行锁等待累积耗时(ms) |
| DML 计数 | Innodb_rows_deleted/inserted/updated |
监控比例变化 | 各类 DML 操作的累计次数 |
[!NOTE] 如何判断 Buffer Pool 大小是否合理? 只看命中率是不够的。更可靠的方法是观察
Innodb_buffer_pool_reads的增长斜率:如果它几乎是一条水平线(单位时间内增长极少),说明当前 Buffer Pool 够用;如果斜率持续走高,就是增大innodb_buffer_pool_size的信号。一般建议设为物理内存的 50%~70%。
-- Buffer Pool 命中率(推荐写法)
SELECT
(1 - A.value / B.value) * 100 AS hit_rate_pct
FROM (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') A,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') B;
-- 为什么不用 INFORMATION_SCHEMA.INNODB_METRICS?
-- 该表需要额外启用,且部分版本中字段名不一致,推荐使用 global_status 视图
[!QUESTION] 思考 假设你的 Buffer Pool 命中率为 99.5%,但这个值是从服务启动至今累计计算的。如果最近一小时内出现了缓存穿透,累计比率会显示出来吗?
答案:不会——累计值会稀释短期异常。正确的做法是用定时采集的 差值 来计算窗口内的实时命中率。
复制延迟指标
主从复制是 MySQL 高可用的基石。延迟超过容忍度,读写分离就会带来数据一致性问题。
| 指标 | SHOW REPLICA / SLAVE STATUS 字段 | 告警阈值 | 说明 |
|---|---|---|---|
| Seconds_Behind_Master | Seconds_Behind_Master |
> 5s 警告,> 30s 严重 | 从库落后主库的秒数(NULL 表示连接断开) |
| IO Thread | Slave_IO_Running (5.7) / Replica_IO_Running (8.0) |
必须 = Yes | IO 线程负责拉取 binlog |
| SQL Thread | Slave_SQL_Running (5.7) / Replica_SQL_Running (8.0) |
必须 = Yes | SQL 线程负责重放事件 |
| Relay Log 空间 | Relay_Log_Space |
突增预警 | 中继日志积压通常意味着大事务 |
-- 标准查看方式(MySQL 8.0.22+ 使用 REPLICA 关键字)
SHOW REPLICA STATUS\G
-- MySQL 8.0+: 基于 GTID 的更精确延迟检测
SELECT
PROCESSLIST_TIME AS delay_seconds,
INFO
FROM performance_schema.threads t
JOIN performance_schema.events_stages_current ess
ON t.THREAD_ID = ess.THREAD_ID
WHERE NAME = 'stage/replication_sql_waiting_for_master_to_send_event';
[!CAUTION] Seconds_Behind_Master 的陷阱 当从库停止时,这个值会变成 NULL 而非一个巨大的数字。因此告警逻辑应同时检查
Slave_IO_Running = No、Slave_SQL_Running = No和Seconds_Behind_Master IS NULL三种情况。
慢查询趋势
慢查询日志不只是排障工具,更是 长期性能趋势 的风向标。每天慢查询数量的突然增加,往往预示着即将发生的系统性问题。
-- 每日慢查询统计
SELECT DATE_FORMAT(event_time, '%Y-%m-%d') AS day,
COUNT(*) AS slow_count,
ROUND(AVG(query_time), 3) AS avg_time,
ROUND(MAX(query_time), 3) AS max_time
FROM mysql.slow_log
GROUP BY day
ORDER BY day DESC
LIMIT 30;
[!TIP] 实操建议 在生产环境不建议直接查
mysql.slow_log(它是表格式存储,数据量大时性能差)。推荐使用 Percona 的 pt-query-digest,它能聚合相似查询并按类型统计,还能输出 HTML 报告。
一个实用的告警规则设计:
flowchart LR
A["Slow Query 阈值\nquery_time > 1s"] --> B{"单条超时 > 10s?"}
B -->|是| C["立即告警\nP1 级别"]
B -->|否| D{"当日累计 > 昨日 2x?"}
D -->|是| E["趋势告警\nP2 级别"]
D -->|否| F["正常记录"]
style C fill:#f99,stroke:#c00
style E fill:#fd9,stroke:#ca0
style F fill:#9f9,stroke:#0a0
这种分级告警能帮你区分「偶发异常」和「系统性恶化」,避免告警疲劳。
告警分级策略
好的监控应该告诉你 现在该做什么,而不是让你从一堆告警里自己判断。
| 级别 | 触发条件示例 | 响应时效 | 通知渠道 |
|---|---|---|---|
| P0 致命 | Slave_SQL_Running = No、磁盘空间 < 5%、Innodb_deadlocks > 0/min | 5 分钟内 | 电话 + 短信 + IM |
| P1 严重 | 延迟 > 30s、Threads_running > CPU×2、QPS 骤降 50%+ | 15 分钟内 | 短信 + IM |
| P2 警告 | 延迟 > 5s、命中率 < 95%、慢查询激增 | 1 小时内 | IM 群 |
| P3 提示 | Buffer Pool 利用率持续下降、表空间增长过快 | 当天处理 | 日报/周报 |
[!QUESTION] 你的 SLA 是什么? 不同的业务对可用性的要求差异巨大:金融系统可能要求 99.999%,而内部工具 99.9% 就足够了。先明确业务目标,再反过来设计告警阈值。不要盲目套用别人的配置。
监控工具选型
| 工具 | 定位 | 优点 | 缺点 |
|---|---|---|---|
| Prometheus + mysqld_exporter | 指标采集 | 云原生标准、告警丰富 | 需自建 Grafana Dashboard |
| PMM (Percona) | 完整监控平台 | 开箱即用、包含 QAN 分析 | 较重,占用资源 |
| Zabbix | 传统监控 | 成熟稳定、报警灵活 | 缺少查询级洞察 |
| Datadog/NewRelic | SaaS 监控 | 零运维、可视化优秀 | 付费、数据出网 |
| 阿里云 RDS 监控 | 云服务内置 | 免费、深度集成 | 锁定云厂商 |
Prometheus + mysqld_exporter 快速搭建
# prometheus.yml 追加
scrape_configs:
- job_name: 'mysql'
static_configs:
- targets: ['localhost:9104'] # mysqld_exporter 端口
# 启动 exporter(MySQL 5.7)
mysqld_exporter \
--config.my-cnf=/etc/mysql/debian.cnf \
--collect.global_status \
--collect.engine_innodb_status \
--collect.perf_schema.tableiowaits
# MySQL 8.0 推荐额外开启 perf_schema 指标
mysqld_exporter \
--config.my-cnf=/etc/mysql/debian.cnf \
--collect.global_status \
--collect.info_schema.innodb_metrics \
--collect.perf_schema.eventswaitssummary
[!NOTE] mysqld_exporter 权限 exporter 需要专门的只读账号,至少授予
REPLICATION CLIENT,PROCESS,SUPER权限即可。切勿用 root 账号运行。
Grafana 关键 Dashboard
以下面板组合作为基线监控,可根据自身需求增减:
1. Overview: QPS, TPS, Connections, Slow Queries
2. InnoDB Buffer Pool: Hit Rate, Dirty Pages, Free Pages
3. Replication: Seconds Behind Master, IO/SQL Thread Lag
4. Disk I/O: Read/Write Latency, Throughput
5. Network: Bytes Sent/Received per Second
6. Memory: Used vs Available
社区推荐的 Dashboard ID(导入到 Grafana 即可):
| 主题 | Dashboard ID | 作者 |
|---|---|---|
| MySQL Overview | 3131 | |
| Percona MySQL | 9967 | Percona |
| mysqld_exporter | 12234 | Prometheus Community |
关联笔记
- hhs/MySQL/20-慢查询日志分析 — pt-query-digest 配合监控系统
- hhs/Redis/09-高级特性 — Redis 监控指标的对比
- hhs/GORM/16-日志与调试 — GORM 级别的慢查询追踪