前言

数据库监控是 DBA 日常工作的基石。一个成熟的监控体系不仅能在故障发生时快速定位根因,更重要的是能在问题恶化前发出预警,实现从被动响应到主动防御的转变。

MySQL 通过 SHOW GLOBAL STATUSSHOW GLOBAL VARIABLESinformation_schemaperformance_schema 以及 sys 库暴露了数百个运行时指标。面对如此庞杂的信息,关键不在于"监控的越多越好",而在于理解每个指标的物理含义、相互关联以及在不同业务场景下的合理阈值

本文将围绕以下八个维度,对 MySQL 监控指标体系进行系统性梳理:

维度核心关注点
服务器级指标实例存活性、吞吐量、负载趋势
连接与线程并发压力、连接池健康度、异常断连
临时对象与慢查询SQL 质量、优化器行为
锁与并发表锁/行锁竞争、死锁频率
网络流量带宽消耗、数据传输规模
InnoDB Buffer Pool内存命中率、I/O 压力
文件与表缓存文件描述符、表缓存效率
复制拓扑主从健康、延迟、数据一致性

约定:本文中所有 SHOW GLOBAL STATUS 返回的变量统一称为"状态变量"(Status Variable),SHOW GLOBAL VARIABLES 返回的称为"系统变量"(System Variable)。状态变量为累计值或瞬时值,系统变量为配置参数


1. 服务器级指标

服务器级指标反映 MySQL 实例的整体运行状态和负载趋势,是监控体系的第一道防线。

1.1 Uptime —— 实例存活时间

1
SHOW GLOBAL STATUS LIKE 'Uptime';
属性说明
类型状态变量(瞬时值)
单位
含义自 MySQL 服务启动以来经过的秒数

监控价值

Uptime 本身不直接指示性能问题,但它的突然归零是最强的异常信号——意味着实例发生了重启。在生产环境中,非计划重启往往伴随着 OOM Killer、崩溃恢复(crash recovery)或误操作。

最佳实践

  • 对 Uptime 设置下降型告警:当 Uptime < 前一次采集值时触发 Critical 告警
  • 结合 Uptime_since_flush_status 判断是否有人手动执行了 FLUSH STATUS
  • 在 Prometheus 中使用 mysql_global_status_uptime 结合 changes() 函数检测重启
# Prometheus 告警规则示例
alert: MySQLRestarted
expr: changes(mysql_global_status_uptime[10m]) > 0
for: 1m
labels:
  severity: critical
annotations:
  summary: "MySQL 实例发生重启"

1.2 Queries 与 Questions —— 吞吐量双指标

1
2
SHOW GLOBAL STATUS LIKE 'Queries';
SHOW GLOBAL STATUS LIKE 'Questions';

这两个指标经常被混淆,但它们的统计口径有本质区别:

指标统计范围
Queries服务器执行的所有语句总数,包括存储过程内部语句、COM_STMT_EXECUTE(预编译语句执行)等
Questions客户端发送到服务器的语句总数,不包含存储过程内部语句

关键差异

1
2
3
4
-- 假设有一个存储过程包含 5 条 SQL
CALL my_procedure();
-- Questions +1(只计 CALL 本身)
-- Queries  +6(CALL + 存储过程内部 5 条)

实际应用

  • Questions 更适合用于衡量"客户端视角的负载"
  • Queries 更适合用于衡量"服务器实际执行的工作量"
  • 如果 Queries - Questions 差值很大,说明业务大量使用了存储过程或预编译语句

1.3 QPS 与 TPS —— 吞吐量速率指标

QPS(Queries Per Second)和 TPS(Transactions Per Second)不是 MySQL 原生暴露的状态变量,而是需要通过两次采样的差值计算得出:

1
2
3
4
5
-- QPS 计算(基于 Questions,客户端视角)
QPS = (Questions_t2 - Questions_t1) / (t2 - t1)

-- TPS 计算
TPS = ((Com_commit_t2 - Com_commit_t1) + (Com_rollback_t2 - Com_rollback_t1)) / (t2 - t1)

为什么 QPS 基于 Questions 而非 Queries?

在多数监控场景中,我们关心的是"客户端对数据库施加了多大压力",而存储过程内部语句属于服务器端的自主行为,使用 Questions 能更准确地反映外部负载。但如果业务大量使用存储过程,则应同时监控基于 Queries 的 QPS 以获取完整视图。

阈值参考

QPS 和 TPS 没有绝对的"标准值",需要根据业务建立基线(Baseline):

场景典型 QPS 范围说明
小型 Web 应用500 ~ 2,000读多写少,多为简单查询
中型业务系统2,000 ~ 10,000混合读写负载
高并发 OLTP10,000 ~ 50,000+需要连接池 + 读写分离

告警策略

  • 静态阈值:设定 QPS 上限(如历史峰值的 1.5 倍),超过触发告警
  • 动态基线:使用环比(同一时间段前一天/前一周的值)检测异常波动
  • 趋势告警:QPS 持续上升且斜率超过阈值时预警,提前发现容量瓶颈

1.4 Com_* —— 语句类型分布

1
SHOW GLOBAL STATUS LIKE 'Com_%';

Com_* 系列变量记录了每种 SQL 命令的执行次数。关键的几个:

变量含义
Com_selectSELECT 语句执行次数
Com_insertINSERT 语句执行次数
Com_updateUPDATE 语句执行次数
Com_deleteDELETE 语句执行次数
Com_commitCOMMIT 执行次数
Com_rollbackROLLBACK 执行次数
Com_begin显式 BEGIN/START TRANSACTION 次数

读写比分析

1
2
3
-- 读写比计算
读比例 = Com_select / (Com_select + Com_insert + Com_update + Com_delete)
写比例 = 1 - 读比例

读写比是决定架构策略的关键依据:

  • 读比例 > 80%:适合读写分离架构,将读流量导向从库
  • 写比例 > 40%:需要关注 InnoDB redo log、binlog 写入性能
  • Com_rollback / Com_commit > 5%:事务回滚率偏高,需排查业务逻辑

2. 连接与线程指标

连接管理是 MySQL 最容易出问题的环节之一。连接数暴增、连接泄漏、频繁断连都是生产环境中的高频故障。

2.1 Threads_connected —— 当前活跃连接数

1
SHOW GLOBAL STATUS LIKE 'Threads_connected';
属性说明
类型状态变量(瞬时值)
含义当前处于连接状态的客户端线程数

注意区分Threads_connected 包含所有已建立连接的线程,无论它们是否正在执行查询。一个空闲连接(Sleep 状态)也计入其中。

相关系统变量

1
SHOW GLOBAL VARIABLES LIKE 'max_connections';  -- 最大允许连接数,默认 151

告警阈值

使用率 = Threads_connected / max_connections × 100%

> 70%  → Warning(开始关注)
> 85%  → Critical(需要立即处理)
> 95%  → Emergency(即将耗尽,新连接会被拒绝)

连接耗尽时的现象

Threads_connected 达到 max_connections 时,新连接将收到 ERROR 1040 (HY000): Too many connections。此时即便是 DBA 也无法登录排查问题(除非预留了 extra_port 或使用 super_read_only 的超级用户连接)。

生产建议

  • 务必设置 extra_max_connections(Percona Server)或预留 SUPER 权限的管理连接
  • 应用层必须使用连接池,避免短连接的创建/销毁开销
  • 监控 SHOW PROCESSLIST 中 Sleep 状态超过 wait_timeout 的僵尸连接

2.2 Threads_running —— 当前并发执行线程数

1
SHOW GLOBAL STATUS LIKE 'Threads_running';
属性说明
类型状态变量(瞬时值)
含义当前正在执行查询(非 Sleep)的线程数

Threads_running 是比 Threads_connected 更敏感的负载指标。大量连接处于 Sleep 状态时 Threads_connected 很高但系统未必有压力;而 Threads_running 突增则意味着数据库正在承受真实的并发负载。

经验公式

正常范围:Threads_running < CPU 核心数 × 2 ~ 3
告警阈值:Threads_running > CPU 核心数 × 5(持续 30 秒以上)

Threads_running 飙升的常见原因

原因排查方向
慢查询堆积检查 SHOW PROCESSLIST,找到执行时间长的查询
锁等待检查 SHOW ENGINE INNODB STATUS 的 SEMAPHORES 和 TRANSACTIONS 段
主从延迟导致读打到主库检查从库状态和读写分离中间件配置
突发流量对比 QPS 曲线,确认是否为正常流量高峰

2.3 Max_used_connections —— 历史最大并发连接数

1
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
属性说明
类型状态变量(高水位标记)
含义自实例启动以来,同时连接的最大客户端数

容量规划核心指标

连接余量比 = Max_used_connections / max_connections × 100%

> 80%  → 需要考虑增大 max_connections 或优化连接池
< 10%  → max_connections 可能设置过大,浪费内存

内存影响:每个连接即使处于 Sleep 状态,也会占用一定的内存资源。MySQL 为每个连接分配的内存包括:

内存分配大小说明
线程栈thread_stack(默认 256K ~ 1M)每个连接固定分配
排序缓冲sort_buffer_size(默认 256K)需要排序时按需分配
连接缓冲read_buffer_size(默认 128K)全表扫描时分配
结果缓冲net_buffer_length(默认 16K)发送结果时分配

如果 max_connections 设置为 5000,而大量连接同时执行排序操作,仅 sort_buffer_size 一项就可能消耗 5000 × 256K ≈ 1.2G 内存。

2.4 Aborted_clients 与 Aborted_connects —— 异常断连

1
2
SHOW GLOBAL STATUS LIKE 'Aborted_clients';
SHOW GLOBAL STATUS LIKE 'Aborted_connects';
指标含义
Aborted_clients客户端成功连接后未正常关闭就断开的次数(如超时、网络闪断、程序崩溃)
Aborted_connects尝试连接 MySQL 失败的次数(如密码错误、权限不足、max_connections 已满)

二者的本质区别

  • Aborted_connects:连接建立阶段失败,客户端从未成功登录
  • Aborted_clients:连接使用阶段异常断开,客户端已经成功登录但未正常退出

告警逻辑

Aborted_connects 增长率 > 10次/分钟 → 告警(可能在遭受暴力破解或应用配置错误)
Aborted_clients  增长率 > 50次/分钟 → 告警(应用连接池配置问题或网络不稳定)

排查 Aborted_connects 高的常见原因

  1. 应用配置了错误的密码或用户名
  2. 来自未授权 IP 的连接尝试(检查 host_cache 表或错误日志)
  3. max_connections 已满导致新连接被拒
  4. SSL/TLS 握手失败

排查 Aborted_clients 高的常见原因

  1. 客户端没有调用 mysql_close() 就退出
  2. wait_timeout / interactive_timeout 设置过短,空闲连接被服务器端强制断开
  3. 网络中间设备(如负载均衡器)的空闲超时比 MySQL 短
  4. PHP 短连接在脚本结束时未正常释放

2.5 Connections —— 累计连接尝试数

1
SHOW GLOBAL STATUS LIKE 'Connections';
属性说明
类型状态变量(累计值)
含义自实例启动以来,尝试连接 MySQL 的总次数(无论成功或失败)

连接创建频率

1
连接创建速率 = (Connections_t2 - Connections_t1) / (t2 - t1)  -- 次/秒

如果连接创建速率持续很高(如 > 100次/秒),说明应用没有使用连接池或连接池配置不当,大量使用短连接。这会带来:

  • TCP 三次握手 + MySQL 认证的额外开销
  • 频繁创建和销毁线程的 CPU 消耗
  • Threads_created 指标持续增长

线程缓存优化

1
2
3
4
5
6
SHOW GLOBAL STATUS LIKE 'Threads_created';
SHOW GLOBAL VARIABLES LIKE 'thread_cache_size';

-- 线程缓存命中率
线程缓存命中率 = (1 - Threads_created / Connections) × 100%
-- 低于 95% 需要增大 thread_cache_size

3. 临时对象与慢查询指标

临时对象的使用情况直接反映 SQL 查询的效率。当优化器无法在内存中完成排序、去重、分组等操作时,会创建临时表甚至将临时表写入磁盘,这是性能下降的重要信号。

3.1 Created_tmp_tables —— 内存临时表创建次数

1
SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';
属性说明
类型状态变量(累计值)
含义查询执行过程中创建的内部临时表总数(包括内存和磁盘)

MySQL 在以下场景会创建内部临时表:

  • GROUP BY 的列没有索引
  • ORDER BYGROUP BY 使用了不同的列
  • DISTINCT 结合 ORDER BY
  • 使用了 UNION(非 UNION ALL
  • 派生表(子查询出现在 FROM 子句中)
  • 某些窗口函数操作

创建内存临时表本身不一定是问题,关键在于它是否被升级为磁盘临时表。

3.2 Created_tmp_disk_tables —— 磁盘临时表创建次数

1
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
属性说明
类型状态变量(累计值)
含义因内存临时表超限而转换为磁盘临时表的次数

磁盘临时表的代价:磁盘 I/O 比内存访问慢 3~5 个数量级。一个本来可以在几毫秒内完成的查询,因为磁盘临时表可能膨胀到几百毫秒甚至几秒。

磁盘临时表比例

1
2
3
4
磁盘临时表比例 = Created_tmp_disk_tables / Created_tmp_tables × 100%

> 25%   需要关注和优化
> 50%   严重问题,需要立即排查

内存临时表转磁盘的触发条件

  1. 临时表大小超过 MIN(tmp_table_size, max_heap_table_size)
  2. 临时表包含 TEXTBLOB 列(MySQL 8.0.13 之前直接使用磁盘)
  3. 临时表包含大于 512 字节的 VARCHAR 列(在某些引擎中)

优化方向

1
2
3
4
5
6
7
-- 检查当前配置
SHOW GLOBAL VARIABLES LIKE 'tmp_table_size';      -- 默认 16M
SHOW GLOBAL VARIABLES LIKE 'max_heap_table_size';  -- 默认 16M

-- 适当增大(注意是 per-query 分配,不宜过大)
SET GLOBAL tmp_table_size = 64 * 1024 * 1024;       -- 64M
SET GLOBAL max_heap_table_size = 64 * 1024 * 1024;   -- 64M

关键认知:调大 tmp_table_size 只是"兜底",根本解决方案是优化 SQL——通过添加合适的索引避免临时表的创建。

3.3 Created_tmp_files —— 临时文件创建次数

1
SHOW GLOBAL STATUS LIKE 'Created_tmp_files';
属性说明
类型状态变量(累计值)
含义MySQL 在操作系统层面创建的临时文件数量

Created_tmp_filesCreated_tmp_disk_tables 不同。前者是 MySQL 内部引擎(如排序算法、binlog 缓存溢出)在文件系统层面创建的临时文件,后者是 SQL 层临时表使用的磁盘引擎。

常见触发场景

  • 大排序操作超出 sort_buffer_size 时的外部排序
  • binlog 缓存溢出(binlog_cache_size 不足时写入临时文件)
  • LOAD DATA 操作的中间文件

监控建议Created_tmp_files 增长速率不宜过快,但绝对值参考意义有限。应结合 sort_buffer_sizebinlog_cache_disk_use 联合分析。

3.4 Slow_queries —— 慢查询累计次数

1
SHOW GLOBAL STATUS LIKE 'Slow_queries';
属性说明
类型状态变量(累计值)
含义执行时间超过 long_query_time 的查询总数

相关系统变量

1
2
3
4
SHOW GLOBAL VARIABLES LIKE 'long_query_time';       -- 慢查询阈值,默认 10 秒
SHOW GLOBAL VARIABLES LIKE 'slow_query_log';         -- 是否开启慢查询日志
SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';    -- 慢查询日志文件路径
SHOW GLOBAL VARIABLES LIKE 'log_queries_not_using_indexes';  -- 是否记录未使用索引的查询

生产环境推荐配置

1
2
3
4
5
[mysqld]
slow_query_log = ON
long_query_time = 1            # 1 秒,而非默认的 10 秒
log_queries_not_using_indexes = ON
log_throttle_queries_not_using_indexes = 60  # 每分钟最多记录 60 条

为什么 long_query_time 要设为 1 秒而非默认的 10 秒?

对于 OLTP 系统,一条查询超过 1 秒已经是"慢"了。10 秒的默认值源自早期 MySQL 版本,当时硬件性能有限。在现代 SSD + 充足内存的环境下,保持 10 秒的阈值会让大量有优化空间的查询逃过监控。

慢查询速率告警

慢查询速率 = (Slow_queries_t2 - Slow_queries_t1) / (t2 - t1)

> 5 次/分钟  → Warning
> 20 次/分钟 → Critical

慢查询分析工具链

工具用途
mysqldumpslowMySQL 自带,按模式聚合慢查询
pt-query-digestPercona Toolkit,功能最强大的慢查询分析工具
sys.statement_analysisMySQL 8.0 sys 库,基于 Performance Schema 的实时统计
1
2
3
4
5
# pt-query-digest 使用示例
pt-query-digest /var/log/mysql/slow.log \
  --limit=20 \
  --order-by=Query_time:sum \
  --output=report

4. 锁与并发指标

锁竞争是数据库性能瓶颈的头号嫌疑人。MySQL 的锁体系分为表级锁行级锁两层,在 InnoDB 引擎下还涉及意向锁、间隙锁、自增锁等细粒度机制。

4.1 Table_locks_immediate 与 Table_locks_waited

1
2
SHOW GLOBAL STATUS LIKE 'Table_locks_immediate';
SHOW GLOBAL STATUS LIKE 'Table_locks_waited';
指标含义
Table_locks_immediate请求表锁后立即获得的次数
Table_locks_waited请求表锁后需要等待才能获得的次数

表锁等待比例

1
2
3
4
表锁等待比例 = Table_locks_waited / (Table_locks_immediate + Table_locks_waited) × 100%

> 0.5%   需要关注
> 2%     存在严重的表锁竞争

容易误解的地方

在纯 InnoDB 环境下,Table_locks_waited 应该非常低甚至为 0。如果这个值持续增长,常见原因包括:

  1. 仍在使用 MyISAM 引擎的表(如某些系统表或历史遗留表)
  2. 显式 LOCK TABLES 语句
  3. ALTER TABLE 等 DDL 操作(在 Online DDL 不支持的场景下会持有元数据锁)
  4. 隐式表锁:即便 InnoDB 使用行锁,MySQL Server 层在某些操作中仍会获取表级意向锁

4.2 InnoDB 行锁指标(引申补充)

对于 InnoDB 引擎,行级锁指标比表级锁更有诊断价值:

1
SHOW GLOBAL STATUS LIKE 'Innodb_row_lock%';
指标含义
Innodb_row_lock_current_waits当前正在等待行锁的线程数
Innodb_row_lock_waits行锁等待的累计次数
Innodb_row_lock_time行锁等待的累计时间(毫秒)
Innodb_row_lock_time_avg行锁等待的平均时间(毫秒)
Innodb_row_lock_time_max行锁等待的最长时间(毫秒)

告警策略

Innodb_row_lock_current_waits > 10(持续 30 秒) → Critical
Innodb_row_lock_time_avg > 500ms                  → Warning
Innodb_row_lock_waits 增长速率 > 100次/分钟       → Warning

深入排查行锁问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- MySQL 8.0:通过 performance_schema 查看当前锁等待
SELECT
  r.trx_id AS waiting_trx_id,
  r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_query,
  b.trx_id AS blocking_trx_id,
  b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_query
FROM performance_schema.data_lock_waits w
JOIN information_schema.innodb_trx r ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID
JOIN information_schema.innodb_trx b ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID;

4.3 死锁监控(引申补充)

1
SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';  -- MySQL 8.0.18+

在低版本中,死锁次数没有直接的状态变量,需要通过以下方式监控:

1
2
3
4
5
6
7
-- 方法一:解析 SHOW ENGINE INNODB STATUS
SHOW ENGINE INNODB STATUS\G
-- 在 LATEST DETECTED DEADLOCK 段查看最近的死锁信息

-- 方法二:开启死锁日志
SET GLOBAL innodb_print_all_deadlocks = ON;
-- 所有死锁信息将写入 error log

死锁告警策略

死锁增长速率 > 1次/分钟  → Warning(检查事务设计)
死锁增长速率 > 10次/分钟 → Critical(业务逻辑可能存在严重的锁竞争)

减少死锁的编码规范

  1. 固定加锁顺序:在一个事务中按主键升序访问行
  2. 缩短事务持有时间:将非数据库操作移到事务外部
  3. 使用合适的隔离级别READ COMMITTEDREPEATABLE READ 产生更少的间隙锁
  4. 控制事务大小:避免单个事务修改过多行

4.4 元数据锁(MDL)监控(引申补充)

元数据锁(Metadata Lock)是 MySQL 5.5 引入的机制,用于保护表结构在查询执行期间不被修改。MDL 问题在生产环境中非常常见,典型场景是:

一个长时间运行的 SELECT 持有 MDL 读锁 → DBA 执行 ALTER TABLE 需要 MDL 写锁 → 所有后续查询排队等待 MDL 读锁 → 雪崩

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- MySQL 8.0:查看 MDL 锁等待
SELECT
  object_type,
  object_schema,
  object_name,
  lock_type,
  lock_duration,
  lock_status,
  owner_thread_id
FROM performance_schema.metadata_locks
WHERE lock_status = 'PENDING';

MDL 监控建议

  • 开启 performance_schema.metadata_locks 表的消费者
  • 对 DDL 操作前检查是否存在长事务:SELECT * FROM information_schema.innodb_trx WHERE trx_started < NOW() - INTERVAL 60 SECOND
  • 使用 pt-online-schema-changegh-ost 替代原生 ALTER TABLE

5. 网络流量指标

网络流量指标帮助 DBA 了解数据库与客户端之间的数据传输规模,对带宽规划、慢查询排查(大结果集)和安全审计都有重要价值。

5.1 Bytes_received 与 Bytes_sent

1
2
SHOW GLOBAL STATUS LIKE 'Bytes_received';
SHOW GLOBAL STATUS LIKE 'Bytes_sent';
指标含义
Bytes_receivedMySQL 从所有客户端接收到的字节总数(即客户端发送的 SQL 语句和数据)
Bytes_sentMySQL 向所有客户端发送的字节总数(即查询结果和响应)

流量速率计算

1
2
3
4
5
-- 接收速率(客户端 → MySQL)
入站流量 = (Bytes_received_t2 - Bytes_received_t1) / (t2 - t1)  -- bytes/s

-- 发送速率(MySQL → 客户端)
出站流量 = (Bytes_sent_t2 - Bytes_sent_t1) / (t2 - t1)  -- bytes/s

流量比分析

出站/入站比 = Bytes_sent / Bytes_received
出站/入站比含义
> 10:1典型的读密集型业务(小 SQL 请求 → 大结果集)
≈ 1:1可能是大量 INSERT/LOAD DATA 操作(入站数据量大)
极高(> 100:1)存在未加 LIMIT 的全表查询,返回了海量数据

告警建议

  • Bytes_sent 速率突增:可能有查询返回了异常大的结果集,检查是否有 SELECT *WHERE 条件的查询
  • Bytes_received 速率突增:可能正在进行大批量 INSERTLOAD DATA INFILE 操作
  • 流量达到网卡带宽的 70%+ 时应告警,特别是在主从复制架构中,binlog 传输也消耗带宽

5.2 Com_* 语句统计(与网络流量的关联)

在第 1 章已介绍了 Com_* 的基本含义,这里从网络流量的角度做补充分析。

每条语句平均消耗的网络带宽

1
2
平均请求大小 = Bytes_received / Questions    -- 每条查询的平均入站字节
平均响应大小 = Bytes_sent / Questions          -- 每条查询的平均出站字节

如果"平均响应大小"持续偏大(如 > 10KB/query),说明存在以下可能:

  1. SELECT 返回了不必要的列(应改为只查需要的字段)
  2. 缺少 LIMIT 限制,返回了过多行
  3. TEXT/BLOB 字段被不必要地包含在结果集中
  4. 未启用结果集压缩(--compress 选项)

在主从复制场景下的网络考量

主库的 binlog 传输到从库的流量也应纳入监控。特别是跨机房复制场景下,binlog 流量可能成为专线带宽瓶颈:

1
2
3
4
5
-- 主库上查看 binlog 写入量
SHOW GLOBAL STATUS LIKE 'Binlog_cache_disk_use';
SHOW GLOBAL STATUS LIKE 'Binlog_cache_use';
SHOW GLOBAL STATUS LIKE 'Binlog_stmt_cache_disk_use';
SHOW GLOBAL STATUS LIKE 'Binlog_stmt_cache_use';

6. InnoDB Buffer Pool 指标

InnoDB Buffer Pool 是 MySQL 最核心的内存组件,所有 InnoDB 表的数据页和索引页都缓存在其中。Buffer Pool 的命中率直接决定了数据库的 I/O 性能——命中就是内存读取(纳秒级),未命中就要从磁盘加载(毫秒级)。

6.1 innodb_buffer_pool_size —— Buffer Pool 总大小

1
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';
属性说明
类型系统变量(配置参数)
单位字节
含义InnoDB 用于缓存数据页和索引页的内存区域大小

sizing 经验法则

场景推荐大小
专用数据库服务器物理内存的 70% ~ 80%
与应用共享服务器物理内存的 50% ~ 60%
容器化部署容器内存限制的 60% ~ 75%

为什么不能设为 100%?

操作系统本身需要内存,MySQL 的其他内存组件(连接缓冲、排序缓冲、binlog 缓存等)也需要内存。如果 Buffer Pool 占用过多,操作系统可能触发 OOM Killer 杀掉 mysqld 进程。

MySQL 8.0 支持在线调整

1
2
3
SET GLOBAL innodb_buffer_pool_size = 8 * 1024 * 1024 * 1024;  -- 调整为 8G
-- 可以通过以下方式监控调整进度:
SHOW STATUS LIKE 'Innodb_buffer_pool_resize_status';

6.2 Innodb_buffer_pool_read_requests —— 逻辑读次数

1
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
属性说明
类型状态变量(累计值)
含义InnoDB 从 Buffer Pool 中读取数据页的请求总次数(即逻辑读

每一次查询需要访问数据页时,InnoDB 首先在 Buffer Pool 中查找。如果找到(缓存命中),Innodb_buffer_pool_read_requests 计数器 +1。

6.3 Innodb_buffer_pool_reads —— 物理读次数

1
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
属性说明
类型状态变量(累计值)
含义InnoDB 无法从 Buffer Pool 满足的读请求次数,需要从磁盘加载数据页(即物理读

6.4 Buffer Pool 命中率 —— 最关键的衍生指标

1
命中率 = (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) × 100%

阈值标准

命中率状态行动
> 99.5%优秀无需调整
99% ~ 99.5%良好关注趋势
95% ~ 99%警告考虑增大 Buffer Pool 或优化查询
< 95%危险立即排查:Buffer Pool 太小、冷启动、或全表扫描大表

命中率低的常见原因分析

原因表现解决方案
Buffer Pool 过小稳定但低于 99%增大 innodb_buffer_pool_size
冷启动重启后命中率低,逐渐恢复使用 innodb_buffer_pool_dump_at_shutdown + innodb_buffer_pool_load_at_startup 预热
全表扫描大表命中率周期性骤降优化查询添加索引、限制结果集
工作集超出内存持续低命中率数据归档或分库分表减小工作集

Buffer Pool 预热配置

1
2
3
4
[mysqld]
innodb_buffer_pool_dump_at_shutdown = ON   # 关闭时保存 Buffer Pool 页面列表
innodb_buffer_pool_load_at_startup = ON    # 启动时加载上次保存的页面
innodb_buffer_pool_dump_pct = 75           # 保存 75% 最热的页面(默认 25%)

6.5 Buffer Pool 补充指标

1
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
指标含义监控价值
Innodb_buffer_pool_pages_totalBuffer Pool 总页数容量基准
Innodb_buffer_pool_pages_data包含数据的页数实际使用量
Innodb_buffer_pool_pages_dirty脏页数量写入压力指标
Innodb_buffer_pool_pages_free空闲页数量内存余量
Innodb_buffer_pool_wait_free等待空闲页的次数> 0 说明 Buffer Pool 严重不足
Innodb_buffer_pool_pages_flushed刷新到磁盘的页数(累计)写入 I/O 压力

脏页比例监控

1
脏页比例 = Innodb_buffer_pool_pages_dirty / Innodb_buffer_pool_pages_total × 100%

脏页比例过高(> 75%)可能导致:

  1. 检查点(Checkpoint)频繁触发,产生大量随机写 I/O
  2. 关闭数据库时需要长时间刷脏页(innodb_fast_shutdown = 0
  3. 崩溃恢复时间变长

相关配置参数:

1
2
SHOW GLOBAL VARIABLES LIKE 'innodb_max_dirty_pages_pct';      -- 默认 90%,触发激进刷脏的阈值
SHOW GLOBAL VARIABLES LIKE 'innodb_max_dirty_pages_pct_lwm';  -- 默认 10%,触发预刷脏的低水位

6.6 InnoDB I/O 补充指标

1
2
3
4
5
6
SHOW GLOBAL STATUS LIKE 'Innodb_data_reads';     -- 物理读操作次数
SHOW GLOBAL STATUS LIKE 'Innodb_data_writes';    -- 物理写操作次数
SHOW GLOBAL STATUS LIKE 'Innodb_data_read';      -- 读取的数据量(字节)
SHOW GLOBAL STATUS LIKE 'Innodb_data_written';   -- 写入的数据量(字节)
SHOW GLOBAL STATUS LIKE 'Innodb_data_fsyncs';    -- fsync 操作次数
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written'; -- redo log 写入量(字节)

IOPS 计算

1
2
 IOPS = (Innodb_data_reads_t2 - Innodb_data_reads_t1) / (t2 - t1)
 IOPS = (Innodb_data_writes_t2 - Innodb_data_writes_t1) / (t2 - t1)

结合磁盘硬件的 IOPS 上限(SSD 一般 10K100K IOPS,HDD 约 100200 IOPS),可以判断存储层是否成为瓶颈。


7. 文件与表缓存指标

MySQL 需要打开文件描述符来访问表数据文件、索引文件、日志文件等。当文件描述符资源不足或表缓存效率低下时,会产生额外的系统调用开销。

7.1 Open_files —— 当前打开的文件数

1
SHOW GLOBAL STATUS LIKE 'Open_files';
属性说明
类型状态变量(瞬时值)
含义MySQL 当前打开的非临时文件数量

7.2 Opened_files —— 累计打开过的文件数

1
SHOW GLOBAL STATUS LIKE 'Opened_files';
属性说明
类型状态变量(累计值)
含义自实例启动以来,打开文件的累计次数

如果 Opened_files 增长速率持续很高,说明文件缓存效率低,MySQL 在频繁地打开和关闭文件。

7.3 Open_files_limit —— 文件描述符上限

1
SHOW GLOBAL VARIABLES LIKE 'open_files_limit';
属性说明
类型系统变量(只读,启动时确定)
含义操作系统允许 mysqld 进程使用的最大文件描述符数量

文件描述符使用率

1
2
3
4
文件描述符使用率 = Open_files / open_files_limit × 100%

> 75%   Warning(需要增大限制)
> 90%   Critical(可能导致 "Too many open files" 错误)

常见的文件描述符耗尽场景

  1. 数据库中有大量表(每个 InnoDB 表至少需要 1 个 .ibd 文件描述符)
  2. 分区表过多(每个分区对应独立的文件描述符)
  3. innodb_file_per_table = ON(默认)时表数量很大
  4. 操作系统层面的 ulimit -n 限制过低

调整方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. 查看当前操作系统限制
ulimit -n

# 2. 临时调整(当前会话)
ulimit -n 65535

# 3. 永久调整(/etc/security/limits.conf)
mysql soft nofile 65535
mysql hard nofile 65535

# 4. systemd 服务单元中设置
# 在 [Service] 段添加:
LimitNOFILE=65535

7.4 Table Cache(表缓存)指标

表缓存是 MySQL Server 层维护的一个缓存结构,用于缓存已打开表的文件描述符和元数据。避免每次查询都重新打开表文件。

1
2
3
4
5
6
SHOW GLOBAL VARIABLES LIKE 'table_open_cache';        -- 表缓存大小(默认 4000)
SHOW GLOBAL STATUS LIKE 'Open_tables';                 -- 当前打开的表数量
SHOW GLOBAL STATUS LIKE 'Opened_tables';               -- 累计打开过的表数量
SHOW GLOBAL STATUS LIKE 'Table_open_cache_hits';       -- 表缓存命中次数
SHOW GLOBAL STATUS LIKE 'Table_open_cache_misses';     -- 表缓存未命中次数
SHOW GLOBAL STATUS LIKE 'Table_open_cache_overflows';  -- 表缓存溢出次数

表缓存命中率

1
2
3
表缓存命中率 = Table_open_cache_hits / (Table_open_cache_hits + Table_open_cache_misses) × 100%

< 85%   需要增大 table_open_cache

表缓存使用率

1
2
3
表缓存使用率 = Open_tables / table_open_cache × 100%

> 95%   需要增大 table_open_cache

表缓存的 sizing 原则

table_open_cache ≈ 数据库中的表总数 × max_connections / 100

但这只是经验估算,实际应根据 Opened_tables 的增长速率和 Table_open_cache_overflows 来动态调整。

table_open_cache_instances 的作用

在高并发环境下,表缓存本身可能成为竞争点(因为需要加互斥锁)。table_open_cache_instances(默认 16)将表缓存分片为多个独立的互斥锁区域,减少锁竞争:

1
2
SHOW GLOBAL VARIABLES LIKE 'table_open_cache_instances';
-- 建议设为 16 或 32(高并发场景)

7.5 table_definition_cache —— 表定义缓存(引申补充)

1
2
3
SHOW GLOBAL VARIABLES LIKE 'table_definition_cache';   -- 默认值取决于表数量
SHOW GLOBAL STATUS LIKE 'Open_table_definitions';      -- 当前缓存的表定义数
SHOW GLOBAL STATUS LIKE 'Opened_table_definitions';    -- 累计打开的表定义数

table_definition_cache 缓存的是 .frm 文件(MySQL 5.7)或数据字典(MySQL 8.0)中的表定义信息。与 table_open_cache 不同,它缓存的是表结构元数据而非打开的文件句柄。

在表数量很多(> 10000)的场景下,应确保 table_definition_cache 足够大以容纳所有表定义。


8. 复制拓扑监控指标

在主从复制(Replication)架构中,复制健康状态的监控至关重要。复制中断或延迟过大会直接影响数据一致性和读写分离的可靠性。

8.1 复制状态核心指标

1
2
3
4
5
-- MySQL 5.7
SHOW SLAVE STATUS\G

-- MySQL 8.0.22+
SHOW REPLICA STATUS\G
指标含义正常值
Slave_IO_RunningIO 线程是否在运行(从主库拉取 binlog)Yes
Slave_SQL_RunningSQL 线程是否在运行(回放 relay log)Yes
Seconds_Behind_Master从库落后主库的秒数0 或接近 0
Last_IO_ErrorIO 线程最后的错误信息
Last_SQL_ErrorSQL 线程最后的错误信息
Last_IO_ErrnoIO 线程最后的错误号0
Last_SQL_ErrnoSQL 线程最后的错误号0

告警策略

Slave_IO_Running  != 'Yes'  → Critical(立即告警)
Slave_SQL_Running != 'Yes'  → Critical(立即告警)
Seconds_Behind_Master > 30  → Warning
Seconds_Behind_Master > 300 → Critical
Seconds_Behind_Master = NULL → Critical(复制可能已断开)

8.2 Seconds_Behind_Master 的局限性

Seconds_Behind_Master(SBM)是最常用的延迟指标,但它有几个重要的局限性需要理解:

1. SBM 的计算原理

SBM = 当前时间 - SQL 线程正在执行的事件的时间戳

这意味着:

  • 如果 SQL 线程暂停了(如手动 STOP SLAVE SQL_THREAD),SBM 会持续增长
  • 如果 IO 线程落后于主库,但 SQL 线程已经回放完所有已接收的 relay log,SBM 显示 0——但实际上是有延迟的
  • 如果主库上有一个大事务执行了 600 秒,SBM 在事务结束前一直是 0,事务提交后瞬间跳到 600

2. 多源复制场景

在多源复制(Multi-Source Replication)中,每个复制通道(channel)有独立的 SBM:

1
SHOW REPLICA STATUS FOR CHANNEL 'channel_name'\G

3. 更精确的延迟监控方案

方案原理优势
pt-heartbeat在主库定期写入心跳时间戳,从库读取并计算差值精确度高,不受 SBM 的各种偏差影响
GTID 位点差比较主库 @@gtid_executed 与从库的差集反映未同步的事务数量,而非时间
performance_schema.replication_applier_statusMySQL 8.0 原生支持无需额外工具,信息全面

pt-heartbeat 部署

1
2
3
4
5
6
# 主库上运行守护进程(每秒写入心跳)
pt-heartbeat --database=percona --update --create-table --daemonize

# 从库上检查延迟
pt-heartbeat --database=percona --monitor
# 输出示例:0.02s, 0.01s, 0.01s, 0.02s

8.3 GTID 相关监控(引申补充)

GTID(Global Transaction Identifier)模式下的复制监控比传统基于文件位点的复制更精确:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-- 主库执行的 GTID 集合
SELECT @@global.gtid_executed;

-- 从库已执行的 GTID 集合
SELECT @@global.gtid_executed;

-- 从库已接收但未执行的 GTID(MySQL 8.0)
SELECT RECEIVED_TRANSACTION_SET 
FROM performance_schema.replication_connection_status;

-- 计算 GTID 差集(从库落后的事务数)
SELECT GTID_SUBTRACT(
  (SELECT @@global.gtid_executed FROM ... ),  -- 主库 GTID
  @@global.gtid_executed                       -- 从库 GTID
) AS lag_gtids;

GTID 监控的优势

  1. 可以精确知道从库落后了多少个事务,而非模糊的"秒数"
  2. 不受主库空闲期的影响(主库无写入时 SBM 始终为 0,无法判断是否真的同步)
  3. 多源复制中可以分别追踪每个源的 GTID 进度

8.4 复制过滤与通道监控(引申补充)

1
2
3
4
5
6
-- 检查复制过滤规则
SHOW REPLICA STATUS\G
-- 关注以下字段:
-- Replicate_Do_DB, Replicate_Ignore_DB
-- Replicate_Do_Table, Replicate_Ignore_Table
-- Replicate_Wild_Do_Table, Replicate_Wild_Ignore_Table

需要监控的复制异常场景

错误码含义常见原因
1062Duplicate entry从库上有人手动插入了数据
1032Can’t find record从库上有人手动删除了数据
1146Table doesn’t existDDL 执行顺序问题或复制过滤配置
1236Could not find binlog主库 binlog 已被清理
2003Can’t connect to MySQL网络问题或主库宕机

8.5 半同步复制监控(引申补充)

如果启用了半同步复制(Semi-synchronous Replication),还需要监控以下指标:

1
SHOW GLOBAL STATUS LIKE 'Rpl_semi_sync%';
指标含义告警阈值
Rpl_semi_sync_master_status半同步是否开启OFF 时告警
Rpl_semi_sync_master_no_tx未等待从库确认就提交的事务数持续增长需关注
Rpl_semi_sync_master_yes_tx等待从库确认后提交的事务数正常应持续增长
Rpl_semi_sync_master_tx_avg_wait_time事务平均等待确认时间> 1000μs 需检查网络

半同步降级告警

当从库响应超时(rpl_semi_sync_master_timeout),半同步会自动降级为异步复制,此时 Rpl_semi_sync_master_status 变为 OFF。这意味着数据安全性降低,必须立即告警:

Rpl_semi_sync_master_status = OFF → Critical
Rpl_semi_sync_master_no_tx 持续增长 → Warning(可能频繁降级)

9. 补充高级指标

除了前面八大维度的核心指标外,以下指标在特定场景下同样具有重要的诊断价值。

9.1 InnoDB Redo Log 与写入性能

1
2
3
4
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';        -- redo log 累计写入字节
SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';              -- 因 redo log buffer 不足等待的次数
SHOW GLOBAL STATUS LIKE 'Innodb_log_write_requests';     -- redo log 写入请求次数
SHOW GLOBAL STATUS LIKE 'Innodb_log_writes';             -- redo log 实际写入次数
指标告警条件含义
Innodb_log_waits > 0 且持续增长Warninginnodb_log_buffer_size 可能不足
redo log 写入速率接近磁盘带宽Warning写入性能瓶颈

redo log 大小计算

MySQL 8.0.30+ 支持动态调整 redo log 大小:

1
2
3
4
5
SHOW GLOBAL VARIABLES LIKE 'innodb_redo_log_capacity';  -- MySQL 8.0.30+
-- 旧版本
SHOW GLOBAL VARIABLES LIKE 'innodb_log_file_size';
SHOW GLOBAL VARIABLES LIKE 'innodb_log_files_in_group';
-- 总 redo log 容量 = innodb_log_file_size × innodb_log_files_in_group

9.2 InnoDB 自适应哈希索引(AHI)

1
2
SHOW GLOBAL STATUS LIKE 'Innodb_adaptive_hash%';
SHOW ENGINE INNODB STATUS\G  -- 查看 INSERT BUFFER AND ADAPTIVE HASH INDEX 段

AHI 是 InnoDB 自动为频繁访问的索引页构建的哈希索引。在某些工作负载下可能造成竞争:

1
2
-- 如果 SEMAPHORES 段显示大量 btr0sea.cc 相关的等待,考虑关闭 AHI
SET GLOBAL innodb_adaptive_hash_index = OFF;

9.3 Handler_* 操作统计

1
SHOW GLOBAL STATUS LIKE 'Handler_%';
指标含义监控价值
Handler_read_first读取索引第一条记录的次数全索引扫描的标志
Handler_read_key基于索引键读取的次数索引利用率的正面指标
Handler_read_next顺序读取索引下一条的次数范围扫描的标志
Handler_read_rnd基于固定位置读取行的次数排序后回表读取
Handler_read_rnd_next全表扫描时逐行读取的次数全表扫描的明确标志

索引利用率评估

1
索引利用率 = Handler_read_key / (Handler_read_key + Handler_read_rnd_next) × 100%

如果 Handler_read_rnd_next 值非常大且持续增长,说明存在大量全表扫描查询,需要结合慢查询日志找出具体 SQL 并添加索引。

9.4 排序相关指标

1
SHOW GLOBAL STATUS LIKE 'Sort_%';
指标含义
Sort_rows累计排序的行数
Sort_scan通过全表扫描进行排序的次数
Sort_range通过范围扫描进行排序的次数
Sort_merge_passes排序合并的趟数(外部排序的标志)

Sort_merge_passes > 0 且持续增长说明排序数据量超出了 sort_buffer_size,MySQL 使用了外部排序(写磁盘临时文件后多路归并)。可以适当增大 sort_buffer_size,但更根本的方案是优化查询避免大数据量排序。

9.5 Select_* 查询方式统计

1
SHOW GLOBAL STATUS LIKE 'Select_%';
指标含义优化方向
Select_scan全表扫描次数添加索引
Select_full_join无索引的 JOIN 全表扫描次数最危险的指标之一,必须优化
Select_range使用范围索引的查询次数正常行为
Select_range_checkJOIN 后检查键使用的次数索引选择性不佳
Select_full_range_join使用范围搜索的 JOIN 次数检查 JOIN 条件

Select_full_join > 0 必须告警——这意味着有 JOIN 查询没有使用任何索引,在大表上这是灾难性的。


10. 监控体系搭建建议

有了指标的理论基础,还需要一套可落地的监控体系。以下是生产环境中主流的 MySQL 监控方案:

10.1 监控技术栈选型

方案组成适用场景
Prometheus + Grafanamysqld_exporter + Prometheus + Grafana云原生/容器化环境,最主流的开源方案
ZabbixZabbix Agent + MySQL 监控模板传统运维团队,资产管理集成需求
PMM(Percona Monitoring and Management)基于 Prometheus + Grafana + QAN深度 MySQL/MongoDB 监控,开箱即用
商业方案Datadog / New Relic / SolarWinds预算充足,需要 SaaS 化管理

10.2 Prometheus + mysqld_exporter 核心配置

1
2
3
4
5
6
7
8
# prometheus.yml 片段
scrape_configs:
  - job_name: 'mysql'
    scrape_interval: 15s
    static_configs:
      - targets: ['mysql-host:9104']
        labels:
          instance: 'prod-master'

mysqld_exporter 关键启动参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
mysqld_exporter \
  --config.my-cnf=/etc/.mysqld_exporter.cnf \
  --collect.global_status \
  --collect.global_variables \
  --collect.slave_status \
  --collect.info_schema.innodb_metrics \
  --collect.info_schema.processlist \
  --collect.info_schema.tables \
  --collect.perf_schema.tableiowaits \
  --collect.perf_schema.tablelocks

10.3 告警分级体系

一套合理的告警体系应该分为三个级别:

级别触发条件响应时效通知方式
P0 - Emergency实例宕机、复制中断、磁盘满5 分钟内响应电话 + 短信 + IM
P1 - Critical连接池耗尽、Buffer Pool 命中率 < 95%、主从延迟 > 5 分钟30 分钟内响应短信 + IM
P2 - Warning慢查询增多、磁盘临时表比例高、锁等待增加工作时间内处理IM + 邮件

10.4 监控指标速查总表

维度指标类型告警阈值
存活性Uptime瞬时值突然归零 → P0
吞吐量QPS(基于 Questions)速率> 历史峰值 1.5 倍 → P2
连接数Threads_connected / max_connections比率> 85% → P1
并发度Threads_running瞬时值> CPU×5 持续30s → P1
连接异常Aborted_connects 增长率速率> 10/min → P2
磁盘临时表Created_tmp_disk_tables / Created_tmp_tables比率> 25% → P2
慢查询Slow_queries 增长率速率> 20/min → P1
表锁等待Table_locks_waited 占比比率> 2% → P2
行锁等待Innodb_row_lock_current_waits瞬时值> 10 持续30s → P1
死锁死锁增长率速率> 1/min → P2
网络出站Bytes_sent 速率速率> 网卡70% → P2
BP 命中率1 - reads/read_requests比率< 95% → P1
BP 空闲页Innodb_buffer_pool_wait_free累计> 0 → P1
文件描述符Open_files / open_files_limit比率> 90% → P1
表缓存Table_open_cache_misses 速率速率持续高 → P2
IO 线程Slave_IO_Running状态!= Yes → P0
SQL 线程Slave_SQL_Running状态!= Yes → P0
主从延迟Seconds_Behind_Master瞬时值> 300s → P1
半同步Rpl_semi_sync_master_status状态OFF → P1

11. 总结

MySQL 监控指标体系不是一个静态的清单,而是一个需要根据业务特点持续演进的动态系统。本文梳理的核心要点:

11.1 指标分层思维

第一层:存活性指标(Uptime、复制状态)
   └── 回答"数据库还活着吗?"

第二层:容量指标(连接数、Buffer Pool、文件描述符)
   └── 回答"资源还够用吗?"

第三层:性能指标(QPS、锁等待、慢查询、命中率)
   └── 回答"数据库健康吗?"

第四层:趋势指标(QPS 趋势、连接数趋势、数据量增长)
   └── 回答"未来会不会出问题?"

11.2 监控的三个原则

  1. 不要只监控绝对值,要监控变化率Slow_queries = 10000 不一定有问题(可能跑了一年),但 Slow_queries 增长了 500/分钟 就是严重异常。

  2. 不要只监控单一指标,要看关联指标Threads_running 飙升时,应同时检查 Innodb_row_lock_current_waits(是否锁等待)、Slow_queries(是否慢查询堆积)、QPS(是否流量突增)。

  3. 不要只依赖监控,还要有基线:没有基线的监控就是盲人摸象。花时间建立每个指标在正常业务周期(工作日、周末、月结、促销)下的基线值,告警才有意义。

11.3 从指标到行动的映射

现象排查路径
应用报 “Too many connections”Threads_connected → max_connections → processlist(Sleep 连接过多?)→ 连接池配置
应用响应变慢Threads_running → Slow_queries → Innodb_row_lock_waits → Buffer Pool 命中率 → 磁盘 I/O
主从延迟突增SBM → 主库 binlog 写入量 → 从库 SQL 线程状态 → 大事务检查 → 并行复制配置
数据库 CPU 100%Threads_running → processlist → 慢查询日志 → EXPLAIN 分析 → 索引优化
OOM 被杀Buffer Pool 大小 → max_connections × per-session 内存 → 临时表内存 → 操作系统可用内存

监控的终极目标不是收集数据,而是在正确的时间向正确的人传递正确的信息,驱动正确的行动