7、MySQL 慢查询排查与优化实战指南

一、认识慢查询

1.1 定义

SQL 执行时间超过 long_query_time 阈值的语句,会被记录到慢查询日志,用于事后排查。

  • 默认值 10 秒,生产建议 1 秒。
  • log_queries_not_using_indexes 可额外抓取未走索引的语句。
  • 慢不等于错:排查要找性价比最高的那条,而不是最慢的那条。

1.2 阈值怎么定(行业参考)

业务类型 建议阈值 典型场景
普通业务系统 1 ~ 2 秒 OA 办公、数据报表
电商领域 0.5 ~ 1 秒 ERP(销售/库存/采购)、商城、医院挂号、制造业 MES
专项高压业务 0.1 ~ 0.5 秒 证券、银行核心、保险投保理赔

1.3 危害

  • 长期占用连接,拖垮连接池。
  • 持锁时间久,引发连锁阻塞。
  • 大事务回放慢,拖出主从延迟。

1.4 三种慢法

类型 典型成因
偶发慢 锁等待、统计信息抖动
持续慢 缺索引、写法差
突发慢 数据量增长、并发突增、参数变更

二、慢查询日志

2.1 常用命令速查

sql 复制代码
SHOW VARIABLES LIKE 'slow_query_log';        -- 是否开启
SHOW VARIABLES LIKE 'slow_query_log_file';    -- 日志文件位置
SHOW GLOBAL STATUS LIKE 'Slow_queries';       -- 慢查询累计数量
SET GLOBAL slow_query_log = ON;               -- 即时开启
SET GLOBAL long_query_time = 1;               -- 设置阈值(秒)

2.2 开启三步

  1. slow_query_log 设为 ON。
  2. long_query_time 设为 1(按业务场景调整)。
  3. log_output 选 FILE 或 TABLE;用 min_examined_row_limit 过滤小表扫描噪音。

2.3 日志记录的关键字段

字段 含义
Query_time 实际执行耗时
Lock_time 锁等待耗时
Rows_sent / Rows_examined 返回行数 / 扫描行数,比值越低越好

另含时间戳、用户、主机等上下文信息。

2.4 扩展参数

  • log_slow_extra:8.0 起记录更多指标(如 Rows_affected、Bytes_sent)。
  • log_slow_admin_statements:记录 DDL 等管理语句。
  • log_slow_replica_statements:从库也记录。

2.5 注意事项

  • SET GLOBAL 即时生效,无需重启,但需写入配置文件才能持久化。
  • log_queries_not_using_indexes 不要盲开,日志量会被放大。
  • slow.log 持续增长,需要配置轮转与定期清理。

三、分析工具

工具 定位 要点
mysqldumpslow 自带、轻量 -s t 按总耗时排序,-t 10 取前十;聚合粗糙,精度有限
pt-query-digest 生产首选 按语句指纹聚合,输出 95 分位与样本,适合周报复盘
performance_schema 8.0 优先 查 events_statements_summary_by_digest,按 sum_timer_wait 排序取 TopN,比日志实时
sys schema 场景化视图 statements_with_full_table_scans、statements_sorting_using_temp_tables

补充:分析主要靠三件套------慢查询日志(找慢 SQL)、mysqldumpslow(聚合统计)、EXPLAIN(看是否合理使用索引)。生产环境下建议把 pt-query-digest 作为标准入口。

四、EXPLAIN 执行计划

4.1 核心字段

字段 读法
id 越大越先执行,相同则从上到下
select_type 出现 DERIVED 需警惕派生表
type 七档访问类型,见下表
key / key_len 实际用到的索引及参与的列数
rows / filtered 估算扫描量与过滤比例,越小越好
Extra 信息量最大,见 4.3

4.2 type 七档

system、const、eq_ref、ref、range、index、ALL,从左到右依次变差:

  • 优秀:system、const、eq_ref
  • 可接受:ref、range
  • 必须优化:index、ALL

4.3 Extra 关键取值

取值 判断
Using index 覆盖索引,好事
Using index condition 索引下推,好事
Using filesort 排序未走索引,需优化
Using temporary 用了临时表,需优化
Using join buffer 被驱动表缺索引

4.4 速查口诀

  • type 从 const 到 ALL,越左越好,出现 ALL 必须优化。
  • key 为 NULL 说明优化器没走索引。
  • Extra 出现 filesort 或 temporary 需要优化。

4.5 进阶手段

  • EXPLAIN ANALYZE(8.0.18+):看真实耗时与真实行数。
  • EXPLAIN FOR CONNECTION <id>:分析正在运行的会话。
  • SHOW WARNINGS:查看优化器改写后的语句。
  • optimizer_trace:查看成本估算过程。

五、语句级剖析

5.1 SHOW PROFILE 用法

sql 复制代码
SET profiling = 1;
SHOW PROFILES;                    -- 列出各语句耗时
SHOW PROFILE FOR QUERY 1;         -- 查看单条各阶段耗时

5.2 各阶段怎么读

  • Sending data 占大头 → 扫描行数多或回表多。
  • Sorting result → 排序耗时。
  • converting HEAP to MyISAM → 临时表已落盘。

局限:8.0 已弃用,转向 performance_schema,由 events_statements_history_long 逐步替代。

六、索引优化

6.1 建索引四原则

  1. 选择性:区分度在 10% 以上才有价值。
  2. 覆盖性:尽量覆盖查询列,消除回表。
  3. 顺序:等值在前、范围在后、排序跟上。
  4. 克制:单表索引控制在 5 ~ 6 个。

6.2 修复索引失效

失效原因 修法
列上做函数运算 把运算移到常量一侧
隐式类型转换(如 varchar 未加引号) 补引号,或对齐列类型
OR 连接无索引列 拆成 UNION
取反条件(!=、NOT IN) 改为正向枚举

6.3 加索引前先算账

  • 回表占比过大时,优化器宁可选全表扫描。
  • 索引越多写越慢,且挤占 Buffer Pool。

6.4 索引维护

  • ANALYZE TABLE 更新统计信息。
  • FORCE INDEX 只作临时救急手段。
  • 定期清理冗余索引与从未被使用的索引。

七、SQL 改写

7.1 深分页

  • 延迟关联:子查询先用覆盖索引取主键,再回表取数据。
  • 游标法:条件改为 id > 上一页末尾值,避免 OFFSET。

7.2 JOIN

  • 被驱动表的关联列必须有索引。
  • 小表驱动大表。
  • 关联字段的字符集与类型保持一致(否则索引失效)。

7.3 子查询

  • 8.0 已有半连接(semi-join)自动改写,一般不必手动转换。
  • 确实慢时再改写为 JOIN。

7.4 通用写法

  • 大事务拆批,每批约 1000 行。
  • 批量插入按 500 ~ 1000 行一批。
  • 只取需要的列,不用 SELECT *。
  • 不对列包函数,让索引可用。

八、配置与架构层优化

8.1 参数配置

参数 建议
innodb_buffer_pool_size 给到物理内存 50% ~ 70%
redo 日志容量 4 ~ 8G,防止刷盘周期卡顿
tmp_table_size 调大,防止临时表落盘

8.2 架构手段(由轻到重)

读写分离 → 缓存挡热点 → 归档历史数据 → 分区表 → 分库分表(最后手段)。

8.3 MySQL 优化四方向

方向 内容
系统层 增加 CPU、内存,换 SSD,重点关注磁盘 IO
数据库配置 调整内存、连接数、日志刷盘参数,让 MySQL 更合理地使用现有资源
SQL 与索引 建立合适索引、优化 SQL、用 EXPLAIN 分析计划
架构与运维 主从复制、读写分离、高可用、备份恢复、监控告警

优化目标始终是三件事:查询更快、运行更稳、故障更少。

九、排查方法论

9.1 数据库突然变慢怎么办(应急响应)

  1. 看服务器资源:CPU、内存、磁盘是否异常。
  2. 看连接:是否连接数过多、打满连接池。
  3. 看 SQL:找出执行时间长的语句。
  4. 看执行计划:用 EXPLAIN 分析索引与调优情况。
  5. 优化:改 SQL、建索引、加缓存或调整架构。

一句话总结:发现问题靠监控,分析问题靠 EXPLAIN,解决问题靠索引、SQL、配置。

9.2 慢查询五步法

步骤 动作
第一步 拿到具体语句与耗时
第二步 EXPLAIN 看执行计划
第三步 对比预估 rows 与实际行数
第四步 定位问题类型:没索引 / 回表多 / 排序差
第五步 改写并验证,纳入监控防复发

9.3 现场辅助命令

sql 复制代码
SHOW PROCESSLIST;                                              -- 查看正在执行的会话
SELECT * FROM performance_schema.data_lock_waits;              -- 查看锁阻塞
SELECT * FROM information_schema.innodb_trx;                   -- 查看长事务

十、监控与预防

10.1 企业主要监控指标

指标 说明
连接数 最大连接数假设 1000,超过 800 报警
QPS / TPS QPS = 每秒查询次数;TPS = 每秒完成的事务数(事务 = 一次完整操作流程)
慢查询数 超过阈值需告警
CPU / 内存 服务器资源是否够用
磁盘空间与 IO 磁盘是否枯竭、IO 是否变慢
主从延迟 从库数据是否落后于主库
锁等待 是否有事务互相等待导致业务卡顿

10.2 上线前规范

  • 开发约束:每表必须有主键。
  • 新 SQL 上线前必须通过 EXPLAIN 评审。
  • 大表变更先在预发环境压测。

10.3 上线后运营

  • 慢查询数量设阈值告警。
  • digest TopN 周报复盘。
  • 优化完成后回归观察至少一个完整业务周期。

十一、典型案例

案例 现象 解法
深分页拖垮接口 LIMIT 100000, 20,每次扫十万行 延迟关联或游标法
隐式转换全表扫 varchar 列查询未加引号 类型对齐,或修改列类型
排序出现 filesort Extra 出现 Using filesort 联合索引把排序列带上
优化器选错索引 rows 预估与实际相差十倍以上 ANALYZE TABLE,仍错则临时 FORCE INDEX

附录、命令速查表

sql 复制代码
-- 采集
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW GLOBAL STATUS LIKE 'Slow_queries';

-- 分析
mysqldumpslow -s t -t 10 /path/to/slow.log
pt-query-digest /path/to/slow.log

-- Top SQL(8.0)
SELECT digest_text, count_star, ROUND(sum_timer_wait/1e12, 2) AS total_sec
FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC LIMIT 10;

-- 定位
EXPLAIN
EXPLAIN ANALYZE
SHOW PROCESSLIST;
SELECT * FROM information_schema.innodb_trx;

-- 维护
ANALYZE TABLE
相关推荐
ss2739 分钟前
为什么PostgreSQL适合AI场景?
数据库·人工智能·postgresql
z06.1 小时前
MySQL索引
数据库·mysql
考虑考虑1 小时前
Posrgresql恢复.backup文件
数据库·后端·postgresql
云祺vinchin1 小时前
中小企业备份软件选型从恢复需求到总体成本评估
数据库·数据备份·中小企业备份·容灾备份软件
Zenova EdgeOS2 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang
KKKlucifer2 小时前
国内数据安全服务的产品智能化转型:大模型赋能下的技术能力重构分析
大数据·数据库·重构
vibecode2 小时前
网站服务器失去响应怎么解决?我把连接超时和读超时这两段计时拆开跑了一遍
运维·服务器
字节跳动开源3 小时前
OpenViking 上线原生 3D 内容理解模型
数据库·开源
迪康Defender4 小时前
终端供应链安全:应用白名单与软件正版化检测落地|终端安全治理实践
运维·服务器·网络·安全·web安全·自动化·运维开发