MySQL 性能排查:现象→指标→定位层→安全缓解
工程笔记
发布 / DavidHLPL
本页回答一个排查问题:看到 MySQL 变慢或卡住时,先观察什么指标,应该把证据归到应用/连接、Server/SQL、InnoDB,还是 OS/硬件层,随后采取什么不会扩大事故的缓解。它保留可复用的因果模型,不把完整命令清单或单次事故过程当作知识本体。
核心机制
1. 现象到定位层
| 现象 | 第一眼指标/证据 | 定位层 | 优先缓解 |
|---|---|---|---|
Too many connections、Sleep 堆积 |
Threads_connected、max_connections、连接来源;Sleep 时间;INNODB_TRX |
应用/连接 | 限制可疑接口并发,修连接池;确认事务后再处理会话。 |
少量 SQL 卡住,大量 Waiting |
SHOW FULL PROCESSLIST、sys.innodb_lock_waits |
InnoDB 锁 / MDL | 找阻塞源,缩短事务;KILL 只对确认的非核心线程使用。 |
| 特定 SQL 变慢 | 慢查询或 digest、rows_examined / rows_sent、EXPLAIN |
Server 优化器 / 索引 | 先核对统计信息、访问类型、数据选择性,再改 SQL/索引。 |
| 周期性卡顿或整体 IO 高 | iostat -xm 1、脏页、Buffer Pool 读/等待 |
OS 磁盘 / InnoDB Buffer Pool | 限制大查询;按证据调整容量、刷盘或读写路径。 |
| 长事务、undo 膨胀 | 事务年龄、History list length |
InnoDB MVCC / undo | 找到并结束不必要的长事务,避免先做破坏性清理。 |
顺序原则:先用 SHOW PROCESSLIST 看队列形态,再看 top/iostat 等资源,最后深入单条 SQL。不要看到慢查询就跳过现场范围直接 EXPLAIN。
2. 连接与锁:等待是因果线索
- 连接数耗尽时,比较
Threads_connected与max_connections,再按user, host聚合;Threads_created持续增长提示连接池复用不足,Aborted_clients高提示连接未正常关闭。 Sleep超过 60 秒不等于无害;若同时持有事务,会阻塞 DDL 并撑大 undo。必须和INNODB_TRX对照。- 行锁在 MySQL 5.7 可从
INNODB_LOCKS/INNODB_LOCK_WAITS看;MySQL 8.0 推荐performance_schema.data_locks、data_lock_waits或sys.innodb_lock_waits。 Waiting for table metadata lock指向 MDL:常见链路是长事务未提交,另一个会话执行ALTER/DROP,随后访问该表的语句排队。- 死锁证据在
SHOW ENGINE INNODB STATUS \G的LATEST DETECTED DEADLOCK;来源给出的修复方向是统一加锁顺序、缩短事务,必要时评估 RR→RC 的隔离级别变化。
3. SQL、索引与事务
- 慢查询可用动态
slow_query_log,或用performance_schema.events_statements_summary_by_digest按指纹聚合;rows_examined / rows_sent远大于 100 是来源给出的典型索引失效信号。 EXPLAIN重点看type、key、rows、filtered和Extra。ALL、key=NULL、Using filesort、Using temporary是进一步定位的信号,不是单独的修复结论。- 来源列出的失效形状包括:函数包列、隐式类型转换、联合索引缺最左前缀、范围条件右侧列、左模糊
LIKE、OR一侧无索引。MySQL 8.0 可用EXPLAIN FORMAT=JSON、EXPLAIN ANALYZE和临时INVISIBLE索引验证假设。 - 长事务链路是“undo 版本不能 purge → history list 增长 → 表/索引膨胀 → 回表扫描变慢”。运行超过 30 秒是来源示例的筛选条件,不是所有系统的通用 SLA。
4. Buffer Pool、IO 与反直觉的索引选择
重点证据来自 SHOW ENGINE INNODB STATUS \G 与三个全局计数:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_wait_free';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
Innodb_buffer_pool_wait_free增量大于 0、Free buffers很低,说明线程在等待可用页;Modified db pages高则要同时看刷盘压力。- 命中率可按
1 - reads / read_requests估算,来源给出的正常参考是> 99%;但命中率高并不排除物理读短时激增,必须同时看绝对增量与wait_free。 - 工作集约为“频繁访问的数据页数 × 16KB”。当热数据大于 Buffer Pool,物理 IO 可能比 CPU 过滤更贵;此时盲目
FORCE INDEX可能选择冷页,反而增加读盘和buf0buf.cc等待。 - IO 层的最小证据是
iostat -xm 1,关注%util、await、r/s、w/s。资源瓶颈未解除时,单纯新增大索引也可能进一步挤压内存。
5. CPU、临时表和安全缓解
CPU 高常见于无索引全表扫、排序/聚合临时表或真实 QPS 过高;Created_tmp_disk_tables 持续增长和 Using temporary 说明临时表落盘方向。复制告警则先看 SHOW REPLICA STATUS \G(MySQL 8.0;raw 说明 5.7 使用 SHOW SLAVE STATUS)。
安全缓解顺序是:应用层限流/降级 → 记录现场 → 确认阻塞源 → 对非核心长查询谨慎 KILL。当 wait_free > 0 且 Buffer Pool 近满时,来源更支持扩容、归档、缓存或读写分离,而不是先强制索引。
适用条件
- 用这套模型处理连接、锁、索引、事务、Buffer Pool、磁盘 IO 和临时表/CPU 的初筛;每个结论都要回到同一时间窗口的指标差值。
- 生产止血优先减少新增负载,保留 processlist、InnoDB status、资源指标和 SQL 样本,再做可回滚的配置/查询调整。
- MySQL 5.7 与 8.0 的锁视图、复制命令和
EXPLAIN ANALYZE能力不同;先确认版本,不能把 8.0 视图直接套到 5.7。 - Buffer Pool 问题适合结合活跃工作集、物理读和等待计数判断;命中率单指标不足以证明内存健康。
不适用与风险
KILL <thread_id>不是通用加速按钮;误杀核心写事务可能引起应用错误或回滚,来源明确要求把它作为最后手段。- 不要用
FORCE INDEX掩盖 Buffer Pool/IO 饱和;“过滤性更好”不代表页仍在内存中。 Free buffers < 100、wait_free > 0、命中率> 99%等是 raw 中的参考信号,受版本、工作负载和采样窗口影响,不能当作无条件阈值。- 动态打开慢日志的
SET GLOBAL在重启后失效;修改innodb_flush_log_at_trx_commit、sync_binlog或 Buffer Pool 会改变一致性/资源风险,必须先做环境评估。 EXPLAIN、ANALYZE TABLE或建索引只能解释/改变优化器路径,不能替代连接池、锁顺序、事务边界或磁盘容量修复。
最小验证
先获取跨层现场:
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS \G
SELECT trx_id, trx_started, trx_state
FROM information_schema.INNODB_TRX
ORDER BY trx_started;
再按症状补一项:
iostat -xm 1
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_wait_free';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
SHOW STATUS LIKE 'Created_tmp%';
- 连接/锁:把 processlist 的
Waiting、Sleep 时间与事务年龄对上;8.0 优先看sys.innodb_lock_waits。 - 索引:仅在范围已知后执行
EXPLAIN SELECT ... \G;MySQL 8.0 的真实成本可用EXPLAIN ANALYZE SELECT ...,但这是版本敏感检查。 - Buffer Pool/IO:记录查询前后计数差值;
wait_free、物理读、await/%util同时恶化时,先按资源瓶颈缓解,不要先加FORCE INDEX。
证据与不确定性
- 来源事实:
legacy-mysql-performance-troubleshooting记录五层定位法、连接/锁/MDL、索引失效形状、长事务与 history list、Buffer Pool 指标和 16KB 工作集估算、FORCE INDEX反例、IO/临时表信号及止血动作。 - 本页综合:把原始维度压缩为“现象→指标→定位层→安全缓解”,并把资源证据放在 SQL 调整之前;这是模型化整理,不是新的监控阈值。
- 未确认项:当前 MySQL 版本、硬件/磁盘、Buffer Pool 大小、真实工作集、事务隔离级别、连接池实现、SQL 样本和指标采样窗口,均需要现场确认。
相关页面
来源与关联文档
研究记录
- 原始文档: src/content/note/zh-cn/mysql-performance-troubleshooting.md
- 来源版本: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
- 证据状态: 有效