小易说IT 小易说IT

MySQL 慢 SQL 排查脚本合集

适用 MySQL 5.7 / 8.0,分为:慢查询日志开启检查、在线抓慢 SQL、分析慢日志、锁 & 事务、索引检查、性能视图排查 ⚠️ 注意:线上执行尽量避开高峰;performance_schema 需要提前开启;部分 SQL 需要 SUPER 权限

一、先检查慢查询配置(必跑)

-- 查看慢查询整体配置
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time'; -- 慢查询阈值,单位秒
SHOW VARIABLES LIKE 'log_queries_not_using_indexes'; -- 是否记录未走索引SQL
SHOW VARIABLES LIKE 'log_output'; -- FILE / TABLE,TABLE则存在mysql.slow_log表

-- 在线开启慢查询(临时,重启失效;生产建议my.cnf持久化)
-- SET GLOBAL slow_query_log = ON;
-- SET GLOBAL long_query_time = 1;
-- SET GLOBAL log_queries_not_using_indexes = ON;

如果 log_output=TABLE,可以直接查表:

-- 查询mysql.slow_log(log_output=table时可用)
SELECT
  start_time,
  query_time,
  lock_time,
  rows_sent,
  rows_examined,
  sql_text
FROM mysql.slow_log
ORDER BY start_time DESC
LIMIT 50;

二、performance_schema 实时抓慢 SQL(不用等慢日志落盘,推荐)

前提:performance_schema=ON,8.0 默认开启;5.7 需要在 my.cnf 配置

-- 查看是否开启statement采集
SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE 'events_statements%';

-- 抓取最近耗时高SQL
SELECT
  EVENT_ID,
  SCHEMA_NAME,
  SQL_TEXT,
  TIMER_WAIT / 1000000000 AS exec_time_ms,
  LOCK_TIME / 1000000000 AS lock_time_ms,
  ROWS_SENT,
  ROWS_EXAMINED,
  ROWS_AFFECTED,
  CREATED
FROM performance_schema.events_statements_current
WHERE TIMER_WAIT / 1000000000 > 100 -- 大于100ms,按需改
ORDER BY TIMER_WAIT DESC
LIMIT 50;

统计TOP 耗时 SQL(汇总聚合,看热点)

SELECT
  DIGEST,
  DIGEST_TEXT,
  COUNT_STAR AS exec_count,
  SUM_TIMER_WAIT / 1000000000 AS sum_time_ms,
  AVG_TIMER_WAIT / 1000000000 AS avg_time_ms,
  MAX_TIMER_WAIT / 1000000000 AS max_time_ms,
  SUM_ROWS_EXAMINED,
  SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
WHERE SUM_TIMER_WAIT > 0
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 30;

DIGEST:SQL 指纹,相同结构不同参数 SQL 会归为一条,非常适合抓热点

三、锁、长事务排查(很多 “慢 SQL” 不是 SQL 本身慢,是被锁等)

1. 查看当前事务 & 长事务

SELECT
  trx_id,
  trx_state,
  trx_started,
  NOW() - trx_started AS trx_duration_sec,
  trx_query,
  trx_mysql_thread_id,
  trx_isolation_level
FROM information_schema.innodb_trx
ORDER BY trx_started ASC;

2. 查看锁等待(阻塞关系)

SELECT
  r.trx_id AS waiting_trx_id,
  r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_sql,
  b.trx_id AS blocking_trx_id,
  b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_sql
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx r ON w.requesting_trx_id = r.trx_id
JOIN information_schema.innodb_trx b ON w.blocking_trx_id = b.trx_id;

3. 查看活跃连接、跑中的 SQL

SHOW PROCESSLIST;
-- 完整版(推荐)
SELECT
  ID,
  USER,
  HOST,
  DB,
  COMMAND,
  TIME,
  STATE,
  INFO
FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep'
ORDER BY TIME DESC;

杀掉阻塞会话:KILL 线程ID;

四、索引检查脚本(找到慢 SQL 后,检查表索引、缺失索引、冗余索引)

-- 查看表索引
SHOW INDEX FROM `库名`.`表名`;

-- 检查没用过的索引(sys库,MySQL8.0自带sys,5.7需要导入sys schema)
SELECT
  OBJECT_SCHEMA,
  OBJECT_NAME,
  INDEX_NAME
FROM sys.schema_unused_indexes;

-- 冗余索引
SELECT * FROM sys.schema_redundant_indexes;

-- 查看索引使用统计
SELECT * FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE INDEX_NAME IS NOT NULL AND SUM_TIMER_WAIT > 0
ORDER BY SUM_TIMER_WAIT DESC;

五、explain 分析模板(拿到慢 SQL 后套这个)

EXPLAIN ANALYZE -- MySQL8.0.18+支持,直接看真实执行耗时;5.7去掉ANALYZE
SELECT xxx FROM xxx WHERE xxx;

快速解读字段

  • type:ALL 全表扫描危险

  • Extra:Using filesort / Using temporary 重点优化

  • rows:预估扫描行数

  • key:实际使用索引

六、命令行工具:mysqldumpslow(分析慢查询日志文件,shell 脚本)

# 慢日志路径通过 show variables like 'slow_query_log_file' 获取
# 取耗时最多的前10条
mysqldumpslow -s t -t 10 /var/lib/mysql/localhost-slow.log

# 取扫描行数最多
mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log

# 筛选带select的慢SQL
mysqldumpslow -s t -t 20 -g "select" /var/lib/mysql/localhost-slow.log

参数说明:

  • -s t:按总耗时;-s at 平均耗时;-s r 返回行数;-s c 执行次数

  • -t N 取 TOP N

  • -g 正则过滤

七、Shell 一键采集脚本(可定时 crontab 输出慢 SQL 快照)

#!/bin/bash
MYSQL_USER="root"
MYSQL_PASS="xxxx"
MYSQL_SOCKET="/tmp/mysql.sock"
LOG_FILE="/tmp/mysql_slow_snapshot.log"
echo "===== $(date '+%Y-%m-%d %H:%M:%S') 慢SQL快照 =====" >> $LOG_FILE
mysql -u$MYSQL_USER -p$MYSQL_PASS -S $MYSQL_SOCKET -e "
SELECT
  DIGEST_TEXT,
  COUNT_STAR,
  SUM_TIMER_WAIT/1000000000 sum_ms,
  AVG_TIMER_WAIT/1000000000 avg_ms,
  MAX_TIMER_WAIT/1000000000 max_ms,
  SUM_ROWS_EXAMINED,
  SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_ms DESC LIMIT 20;
" >> $LOG_FILE
echo -e "\n" >> $LOG_FILE

八、排查标准流程(配套脚本使用)

  1. 看 processlist:是否有长 SQL / 锁等待

  2. 看 innodb_trx + innodb_lock_waits:区分是慢还是阻塞

  3. performance_schema digest 抓 TOP 热点 SQL 指纹

  4. explain 分析单条 SQL 执行计划

  5. 检查表索引:缺失、冗余、从未使用索引

  6. mysqldumpslow 离线分析历史慢日志

  7. 业务侧评估:N+1、深分页、大 in、无索引

九、踩坑提醒

  1. performance_schema 会增加少量性能开销,线上不要一直开全量采集,用完可以关闭

  2. log_queries_not_using_indexes 打开后,小表全扫描也会大量刷慢日志,容易淹没真实慢 SQL

  3. 长事务、gap 锁、自锁,表象也是 SQL 卡住,不一定是索引问题

  4. MySQL 5.7 没有 EXPLAIN ANALYZE