跳到正文

MySQL 监控指标与告警 ​

一句话:MySQL 的雪崩很少始于 CPU 打满,而是从连接排队、缓冲池命中率、Undo 回收和复制延迟这四个慢变量开始;看斜率和组合,比看单点峰值更早发现问题。适用版本:MySQL 8.0 / 8.4(InnoDB),阈值都是起点,必须按业务基线校准。

为什么需要 ​

监控要回答的不是「现在有没有事」,而是「再过十分钟会不会有事」。QPS、P99、错误率是结果指标,等它们变坏时用户已经受影响;连接排队、缓冲池命中率、History list、复制延迟是先兆指标,给的是处置窗口。MySQL 的指标还分瞬时值和累计值:Threads_connected、Innodb_history_list_length 是瞬时值;Innodb_buffer_pool_reads、Innodb_row_lock_waits 是重启清零的累计值。累计值必须做单位时间增量,拿绝对值设阈值一定误报。下面按「指标反映什么 → 为什么是先兆 → 恶化后怎么雪崩」分层。

核心机制 ​

连接层:先看排队,再看总量 ​

指标反映什么先兆含义
Threads_connected当前连接数,含空闲连接持续逼近 max_connections(8.0 默认 151)说明池在堆积
Threads_running非 Sleep 状态的线程数持续高于 CPU 核数或基线两倍,说明查询在排队,不等于 CPU 忙
Max_used_connections启动以来的连接高水位等于 max_connections 说明已经撞过上限
Aborted_connects握手/认证失败的连接次数突增常见于密码错误、DNS、网络抖动或连接池配置错误
Connection_errors_max_connections因连接上限被拒绝的次数一旦非零,说明入口已经饱和,应用正在收到连接错误

Threads_connected 高而 Threads_running 低,通常是空闲连接或连接池泄漏;两个都高才是真实压力。雪崩路径是:查询变慢 → 连接持有时间变长 → 连接数逼近上限 → 新连接被拒绝或超时 → 应用重试 → 请求量再放大。

InnoDB:缓冲池、刷脏与 Undo ​

指标反映什么先兆含义
命中率 1 - ΔInnodb_buffer_pool_reads / ΔInnodb_buffer_pool_read_requests逻辑读中有多少落到磁盘长窗口持续下降,说明工作集超出缓冲池,物理读开始放大
Innodb_buffer_pool_pages_free空闲页数预热后接近 0 是正常的;配合 Innodb_buffer_pool_wait_free > 0 才说明没有页可淘汰
Innodb_data_pending_fsyncs待完成的 fsync 数持续非零说明刷盘排队,磁盘已经跟不上
Innodb_log_waits等待 log buffer 空间的次数增量持续大于 0,说明提交突发或 log buffer 偏小
Innodb_history_list_length待 Purge 的 Undo 版本数持续上涨说明有老 ReadView 或 Purge 被 IO 拖住
Innodb_row_lock_waits / Innodb_row_lock_time行锁等待次数 / 累计毫秒增量上升说明热点行冲突加剧;Innodb_row_lock_current_waits 是当前等待数

四条因果链要连起来看:长事务持有 ReadView → Undo 版本不能回收 → History list 增长 → Undo 表空间和 ibdata 膨胀 → 磁盘压力反过来拖慢 Purge;缓冲池命中率下降 → 物理读放大 → 查询 P99 上升 → 连接堆积;行锁等待 → 事务变长 → Undo 更多;长事务不提交 → DDL 拿不到 MDL 排他锁 → 后续 DML 排在 DDL 后面,表现为假死。

复制:延迟不只是秒数 ​

指标反映什么先兆含义
Seconds_Behind_Source(8.0.22+,旧名 Seconds_Behind_Master)回放时间落后源库的秒数为 NULL 说明复制线程没跑;为 0 也可能是 IO 线程停了但 SQL 线程恰好追平
Relay_Log_SpaceRelay log 占用字节数持续增长说明 SQL 线程回放慢于 IO 线程接收
Replica_IO_Running / Replica_SQL_Running两个复制线程状态任一不是 Yes,延迟秒数已经失去意义,应立即告警
GTID 差集 Retrieved_Gtid_Set 与 Executed_Gtid_Set已收到但未执行的事务差集扩大就是真实回放缺口;再看 replication_applier_status_by_worker 的 LAST_ERROR_MESSAGE

复制延迟会反噬主库:从库读到旧数据 → 业务重试或补偿 → 更多写请求打回主库 → 主库更忙、延迟更大。Relay_Log_Space 同时会推高从库磁盘。

SQL 层与 OS 层:放大器的两端 ​

  • 慢查询日志:slow_query_log 8.0 默认 OFF,long_query_time 默认 10 秒,log_queries_not_using_indexes 默认 OFF;直接打开未走索引记录可能刷爆日志,先配 min_examined_row_limit。
  • performance_schema.events_statements_summary_by_digest:看 SUM_TIMER_WAIT(皮秒)、SUM_ROWS_EXAMINED / SUM_ROWS_SENT、SUM_NO_INDEX_USED(全表扫描/未用索引)、SUM_CREATED_TMP_DISK_TABLES;扫描行数远大于返回行数说明索引或写法有问题,临时表落盘说明排序/分组超出内存。
  • OS:top / vmstat 1 看 CPU、可用内存和 si/so(swap 有进出就是危险信号);iostat -x 1 看 await、%util、r/s、w/s(IOPS)。NVMe 上 %util 可能因并行度高而失真,要和 IOPS、延迟一起看。

雪崩链路 ​

这张图有两条正反馈:一条从长事务出发,经 Undo 和磁盘回到 IO;另一条从查询变慢出发,经连接堆积和应用重试回到长事务。处置时打断任意一条都比「加机器」快;两条都不断,扩容只会把雪崩推迟几分钟。

最小上手 ​

sql
-- 连接、缓冲池、Undo、行锁:先看瞬时值和累计值
SHOW GLOBAL STATUS WHERE Variable_name IN (
  'Threads_connected','Threads_running','Max_used_connections','Aborted_connects',
  'Connection_errors_max_connections','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests',
  'Innodb_buffer_pool_pages_free','Innodb_buffer_pool_wait_free','Innodb_data_pending_fsyncs',
  'Innodb_log_waits','Innodb_history_list_length','Innodb_row_lock_waits',
  'Innodb_row_lock_time','Innodb_row_lock_current_waits'
);
SHOW ENGINE INNODB STATUS\G          -- 看 TRANSACTIONS 段的 History list length
SHOW REPLICA STATUS\G                -- 8.0.22+;旧版本用 SHOW SLAVE STATUS

-- 谁在跑、谁在等
SELECT thd_id, conn_id, user, db, command, time, state, rows_examined,
       rows_sent, tmp_disk_tables, full_scans, LEFT(current_statement, 80) AS stmt
FROM sys.session WHERE command <> 'Sleep' ORDER BY time DESC LIMIT 20;

SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT / 1e12, 2) AS total_sec,
       SUM_ROWS_EXAMINED, SUM_ROWS_SENT, SUM_NO_INDEX_USED, SUM_CREATED_TMP_DISK_TABLES
FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
promql
# 缓冲池命中率(5 分钟)
100 * (1 - rate(mysql_global_status_innodb_buffer_pool_reads[5m]) / clamp_min(rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]), 1))

# 连接使用率
100 * mysql_global_status_threads_connected / mysql_global_variables_max_connections

复制延迟的 exporter 指标名随 mysqld_exporter 版本变化(旧版常见 mysql_slave_status_seconds_behind_master),以目标实例 /metrics 的实际输出为准。

生产动作 ​

告警起点阈值说明
连接使用率与错误Threads_connected / max_connections > 80% 持续 5 分钟,或连接错误增量突增先查泄漏、慢 SQL、认证和网络,不要只调大上限
运行线程Threads_running 高于 CPU 核数或基线两倍,持续 5 分钟结合 Threads_connected 判断是排队还是空连接
复制延迟Seconds_Behind_Source > 30s 或持续增长线程不是 Yes 时立即告警,不看秒数
History list> 100 万 且持续上涨起点值,先查长事务和 Purge 线程
行锁与长事务等待增量超基线,或事务打开 > 60s用 sys.innodb_lock_waits 找阻塞源
磁盘与 IO使用率 > 80%,或 await 超基线两倍且 %util > 80%同时看 binlog、undo、relay log 的增长
慢查询/全表扫描digest 增量相对基线突增看 SUM_ROWS_EXAMINED 和 SUM_NO_INDEX_USED
命中率与刷盘长窗口低于 99%(OLTP 起点)且下降,或 Innodb_data_pending_fsyncs 持续非零大范围扫描也会拉低命中率;刷盘排队先看磁盘,再考虑 log buffer

处置顺序默认是「先限流止血,再定位,最后扩容」:

  1. 保护入口:应用侧限流、关闭重试风暴、降级非核心查询。连接池满先查泄漏,直接调大 max_connections 往往把线程调度和内存压力放大。
  2. 打断链路:确认后 kill 长事务或阻塞源,释放 MDL;不要批量 kill 所有等待者,否则可能反复重试。
  3. 定位:用 digest 和 OS 指标判断是读放大、锁、复制还是磁盘;不要凭单个峰值下结论。
  4. 扩容/改配置并复盘:读瓶颈加只读副本或缓存,写瓶颈升配或分片;innodb_buffer_pool_size 调整通常要重启,救不了当前故障。恢复后把触发条件和处置动作写回 runbook。

边界 ​

  • 指标名、默认值、复制术语随版本变化:8.0.22+ 用 SHOW REPLICA STATUS、Seconds_Behind_Source;8.4 继续沿用 source/replica 术语,旧语法是否保留以 8.4 手册为准。
  • 累计值重启清零,必须用 rate/delta;跨实例比较要用比率,不要比绝对值。
  • 命中率不是越高越好,也不是低就一定故障;全表扫描、预热期、小工作集都会影响它。
  • Seconds_Behind_Source 是近似值,时钟偏差、SQL 线程空闲、大事务都会让它失真;关键业务用 GTID 差集或心跳表补充。监控也替代不了业务指标,用户侧 P99 和错误率才是最终判据。

优缺点与扬长避短 ​

优点是单主 OLTP 的写入路径清晰,InnoDB 的 B+ 树索引、事务和崩溃恢复成熟;异步/半同步/Group Replication 三种复制形态都有,备份、代理、云托管和 exporter 生态完整。代价是写扩展依赖分库分表,大事务和 DDL 成本高(Undo 与 MDL),分析型查询和向量检索弱(8.0/8.4 无原生向量索引,需要外部系统)。

场景判断原因
单库 OLTP、强事务、读多写少适用点查、事务、读写分离路径成熟
写入需要横向扩展单实例不适用要提前做分库分表,不能靠加从库解决写瓶颈
大事务、频繁 DDL不适用Undo 膨胀、MDL 排队,DDL 窗口难控
复杂分析、向量检索不适用应拆到 OLAP/向量系统,不要在主库硬跑
需要成熟托管和运维工具适用生态完整,排障资料多

扬长是把事务和点查留在 MySQL;避短是把分析、搜索、向量拆出去,并在业务早期就设计分片键和归档策略。

讲清楚 ​

MySQL 监控要分四层看:连接层看 Threads_running 和连接使用率,判断是否在排队;InnoDB 层看缓冲池命中率、Innodb_data_pending_fsyncs、History list 和行锁等待,判断读放大、刷盘和 Undo 回收;复制层看 Seconds_Behind_Source、Relay_Log_Space、两个复制线程和 GTID 差集;SQL/OS 层看 digest 的扫描行数、临时表落盘和磁盘 await。雪崩不是单点,而是「长事务 → Undo 不回收 → 磁盘压力」和「命中率下降 → P99 上升 → 连接堆积 → 重试」两条正反馈。处置先限流、杀阻塞源,再定位,最后才扩容;扩容对锁等待和复制延迟通常无效。选型上,MySQL 适合单主 OLTP 和成熟运维体系,写扩展、大事务、分析与向量检索是它的边界。

把一门技术讲透到既懂也会用