调优五步法
-
01
建立基线
Performance Insights 看 DB Load 按等待事件分解,明确到底是 CPU、IO 还是锁在拖后腿。
交付物:等待事件分布图、Top SQL 清单、负载基线
1-2 天
-
02
定位 Top SQL
按总耗时排序而不是单次耗时。一条 50ms 但每秒执行 2000 次的查询,比一条 3 秒的报表查询影响大得多。
交付物:按总耗时排序的 SQL 清单、执行计划分析
2-3 天
-
03
索引与查询改写
补复合索引、消除全表扫描、拆分大事务、避免 SELECT *。改动先在只读副本上验证执行计划。
交付物:索引变更清单、SQL 改写方案、回归测试结果
1-2 周
-
04
参数与连接调优
调整 buffer pool、连接数上限、超时参数;引入连接池或 RDS Proxy 减少连接开销。
交付物:参数组变更、连接池配置、压测对比
3-5 天
-
05
读写分离与缓存
把报表和统计类查询导到只读副本,热点数据放 ElastiCache,从根上减少数据库压力。
交付物:读写分离方案、缓存策略、命中率监控
1-3 周
定位 MySQL 慢查询与缺失索引
-- 1. 按总耗时排序的 Top SQL(需开启 performance_schema)
SELECT
DIGEST_TEXT AS sql_pattern,
COUNT_STAR AS exec_count,
ROUND(SUM_TIMER_WAIT / 1e12, 2) AS total_sec,
ROUND(AVG_TIMER_WAIT / 1e9, 2) AS avg_ms,
SUM_ROWS_EXAMINED AS rows_examined,
SUM_ROWS_SENT AS rows_sent,
ROUND(SUM_ROWS_EXAMINED / GREATEST(SUM_ROWS_SENT, 1), 1) AS scan_ratio
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
-- 2. 全表扫描严重的语句(scan_ratio 越高越可疑)
SELECT
DIGEST_TEXT,
COUNT_STAR,
SUM_NO_INDEX_USED AS no_index_count,
SUM_NO_GOOD_INDEX_USED AS bad_index_count
FROM performance_schema.events_statements_summary_by_digest
WHERE SUM_NO_INDEX_USED > 0
ORDER BY SUM_NO_INDEX_USED DESC
LIMIT 20;
-- 3. 从未被使用的索引(可考虑删除,减少写放大)
SELECT
object_schema, object_name, index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND index_name <> 'PRIMARY'
AND count_star = 0
AND object_schema NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY object_schema, object_name;
-- 4. 表级 IO 热点
SELECT
object_schema, object_name,
count_read, count_write,
ROUND(sum_timer_read / 1e12, 2) AS read_sec,
ROUND(sum_timer_write / 1e12, 2) AS write_sec
FROM performance_schema.table_io_waits_summary_by_table
WHERE object_schema NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY (sum_timer_read + sum_timer_write) DESC
LIMIT 15;
第三条查询要注意:统计窗口从实例启动开始,刚重启过的实例数据不可信。至少观察一周。
常见等待事件与对策
| 等待事件 | 含义 | 常见原因 | 对策 |
|---|---|---|---|
| CPU | 计算资源饱和 | 低效查询、缺索引、排序临时表 | 优化 SQL、补索引,最后才考虑升配 |
| io/table/sql/handler | 表数据读取 | 全表扫描、buffer pool 太小 | 补索引、增大 buffer pool |
| synch/mutex/innodb | 内部锁竞争 | 高并发写同一热点行 | 拆分热点、批量合并写、改用队列削峰 |
| lock/table/sql/handler | 表锁等待 | 长事务、DDL 阻塞 | 缩短事务、DDL 走 gh-ost 或 pt-osc |
| io/file/innodb/innodb_log_file | redo 日志写入 | 写入量大、IOPS 不足 | 提高卷 IOPS、合并小事务 |