Appearance
10|性能调优
前面讲了索引、InnoDB、事务锁。这一篇把它们综合起来——MySQL 变慢时怎么定位、怎么调。
最关键的一点先说在前面:MySQL 慢了,不是上来就调参数。 大多数时候,问题出在 SQL 本身、锁、或物理资源不够,而不是参数不对。从现象出发,按顺序排除,比上来就改配置可靠得多。
一、性能排查的顺序
MySQL 出问题,按这个顺序查:
text
第 1 步:系统资源
top / iostat -x 1 / free -h
磁盘和 CPU 有没有被干满。磁盘 await 上百毫秒,先解决磁盘问题。
第 2 步:连接状态
SHOW FULL PROCESSLIST
有没有堆积?卡在什么状态?有没有大量 lock wait?
第 3 步:慢 SQL
慢日志或 sys.statement_analysis
什么 SQL 在慢?扫了多少行?
第 4 步:锁等待
innodb_trx + innodb_lock_waits
是不是有人在堵着?(第 9 篇讲过)
第 5 步:Buffer Pool
Innodb_buffer_pool_read%
是不是物理读太多?(第 8 篇讲过)
第 6 步:错误日志
tail error.log
有没有异常?每一步都指向一个具体动作——磁盘慢换磁盘、索引没走补索引、锁等待 kill 阻塞源、连接不够限流。查完一圈都说"看起来没问题",说明突破口还没找到,别放弃继续查。
二、系统资源:先看硬件够不够
MySQL 跑在机器上,机器资源打满了,再怎么调 MySQL 参数也没用。
bash
top # CPU 和内存
iostat -x 1 # 磁盘 IO,看 %util 和 await
free -h # 内存磁盘 await 上百毫秒(正常几毫秒),说明磁盘是瓶颈——要么磁盘本身慢,要么 IO 太密集。MySQL 是 IO 密集型应用,磁盘慢直接影响所有查询。
CPU 打满了看是哪个进程——如果是 mysqld,看是不是有全表扫描或大量计算的 SQL。内存看 free 和 buffer/cache,Buffer Pool 占大头正常。
三、连接状态:看连上来的人在干什么
sql
SHOW FULL PROCESSLIST;列出所有连接在干什么。按状态归类:
sql
SELECT user, host, db, command, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY user, host, db, command
ORDER BY cnt DESC;| 看到的现象 | 大概率原因 | 处理 |
|---|---|---|
| 大量 Sleep | 连接池空闲连接太多,开了不用 | 联系应用缩连接池 |
| 大量 Query 且执行时间长 | 慢 SQL 堆积 | 找慢 SQL,补索引或限流 |
大量 Waiting for lock | 锁等待 | 找阻塞源,kill |
| 某来源 IP 连接暴涨 | 应用重试风暴或连接泄漏 | 限流那个来源 |
连接数打满
sql
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';连接数接近上限,第一反应往往是调大 max_connections。但这只是让更多人排队,不解决根本问题。就像高速堵车,不是把收费站口从 5 个开到 10 个就解决——要看出事的是收费站本身,还是前面的路堵了。
连接数打满先看连上来的人在干什么——大量 Sleep 是连接池开太大、大量慢 Query 是 SQL 没优化。对症下药,而不是无脑调大连接数。
四、用 sys schema 定位慢查询
8.0 自带的 sys 库,不用记复杂的 information_schema 表关联,直接查:
sql
-- 最耗时的 10 条 SQL
SELECT * FROM sys.statement_analysis
ORDER BY total_latency DESC LIMIT 10\G
-- 全表扫描的查询
SELECT * FROM sys.statements_with_full_table_scans LIMIT 10\G
-- IO 压力最大的表
SELECT * FROM sys.schema_table_statistics
ORDER BY total_latency DESC LIMIT 10\Gstatement_analysis 按 SQL 模式汇总——执行次数、平均耗时、扫的行数。找到最耗时的 SQL,再 EXPLAIN 看执行计划(第 7 篇),补索引或改查询。
statements_with_full_table_scans 直接列出全表扫描的查询——这些是最该优化的,几百万行全扫。
五、Buffer Pool 调优
第 8 篇讲过 Buffer Pool 是工作台。调优看命中率:
sql
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';命中率低于 99%,说明工作台太小。调大 innodb_buffer_pool_size——机器只跑 MySQL 给到内存 70%-80%。8.0+ 在线调不用重启。
但注意顺序:先看索引,再看 Buffer Pool。 查询走全表扫描时,Buffer Pool 再大也没用——扫的还是几百万行。先把索引问题解决(EXPLAIN 看 type 是不是 ALL),再考虑 Buffer Pool。
六、参数调优的取舍
几个生产常用参数,都有取舍:
| 参数 | 作用 | 调整的取舍 |
|---|---|---|
innodb_buffer_pool_size | Buffer Pool 大小 | 调大查询快,但挤占其他进程 |
innodb_flush_log_at_trx_commit | redo log 刷盘策略 | 1 最安全但慢,2 快但崩溃可能丢 1 秒 |
sync_binlog | binlog 刷盘 | 1 最安全,0 快但崩溃可能丢 binlog |
max_connections | 最大连接数 | 调大让更多连接,但不解决慢查询 |
innodb_flush_log_at_trx_commit=1 和 sync_binlog=1 是"双 1"——最安全的配置,每次事务都刷盘。核心业务库保持双 1,性能有压力时再考虑调低(接受可能丢少量数据的风险)。
参数调优是最后一步。前面 SQL 优化、索引、锁排查做完,还慢,才考虑调参数。多数情况调参数是给已有问题打补丁,不解决根因。
七、一句话记排查关键
MySQL 慢 → 先看索引,再看锁,再看 Buffer Pool,最后看磁盘。
调参数是兜底,不是首选。从现象出发,按顺序排除,找到真正的瓶颈再动手。
性能调优之后
到这里 InnoDB 部分三篇(存储引擎、事务锁、性能调优)讲完了。接下来是账号权限——谁能连、能干什么。这是安全和合规的基础。