MySQL慢查询诊断实战:从EXPLAIN分析到索引优化的完整调优手册

慢查询是数据库性能问题的第一信号

线上MySQL出现性能瓶颈,90%的情况都能从慢查询日志里找到根因。一条慢查询拖垮整个实例的事每天都在发生——不是因为SQL写得离谱,而是因为缺少系统化的诊断方法。从慢查询发现、EXPLAIN解读、到索引优化,这条链路走通了,大部分性能问题都能在30分钟内定位清楚。

慢查询日志的配置与采集

先确保慢查询日志已开启:

-- 查看当前配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5;    -- 500ms以上记录
SET GLOBAL log_queries_not_using_indexes = 'ON';
SET GLOBAL min_examined_row_limit = 100;

-- 持久化到my.cnf
[mysqld]
slow_query_log = 1
long_query_time = 0.5
slow_query_log_file = /var/log/mysql/slow.log
log_queries_not_using_indexes = 1

用pt-query-digest分析慢日志,比手动看高效得多:

# 安装Percona Toolkit
apt install percona-toolkit -y

# 分析最近1小时的慢查询,Top 10
pt-query-digest /var/log/mysql/slow.log --since '1h' --limit 10

# 输出示例:
# Rank Query ID        Response time  Calls  R/Call  V/M
# ==== ============== ============== ====== ======= ====
#    1 0x5A3B2C1D...   125.4 52.1%    342   0.37   0.01
#    2 0x8E7F6A5B...    89.2 37.0%    128   0.70   0.12

锁定Rank靠前的Query ID,提取原始SQL进行分析。

EXPLAIN执行计划逐字段解读

拿到慢SQL后,第一步EXPLAIN:

EXPLAIN SELECT o.id, o.order_no, u.name, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= '2026-07-01'
  AND o.status = 'PAID'
  AND u.region = '华东'
ORDER BY o.created_at DESC
LIMIT 50;

EXPLAIN输出的关键字段:

type(访问类型)— 出现ALL(全表扫描)或index(全索引扫描)就是危险信号。性能排序:system > const > eq_ref > ref > range > index > ALL。生产查询至少要达到ref级别,range是底线。

key(实际使用的索引)— NULL意味着未用索引,必须排查原因。

rows(预估扫描行数)— 值远大于实际返回行数说明索引效率低。

Extra(额外信息)— Using filesort、Using temporary、Using where都是需要优化的信号。

索引优化的六条实战规则

规则1:最左前缀匹配决定索引是否生效

联合索引(region, status, created_at),查询条件WHERE status='PAID' AND created_at > '2026-07-01'无法使用该索引——因为跳过了最左列region。调整查询条件顺序或重建索引。

规则2:范围查询之后的列无法走索引

联合索引(status, created_at, user_id),查询WHERE status='PAID' AND created_at > '2026-07-01' AND user_id=100,只有status和created_at走索引,user_id无法利用索引。解法:把等值条件放前面,(status, user_id, created_at)

规则3:函数操作导致索引失效

-- 索引失效:对列使用函数
SELECT * FROM orders WHERE DATE(created_at) = '2026-07-24';

-- 索引生效:改写为范围查询
SELECT * FROM orders
WHERE created_at >= '2026-07-24 00:00:00'
  AND created_at < '2026-07-25 00:00:00';

规则4:隐式类型转换导致索引失效

user_id是varchar类型,查询WHERE user_id = 12345会触发隐式转换,索引失效。必须WHERE user_id = '12345'

规则5:覆盖索引消除回表

查询只需要索引包含的列时,Extra显示Using index,不回表读数据行:

-- 创建覆盖索引
ALTER TABLE orders ADD INDEX idx_cover (status, created_at, order_no);

-- 查询只select索引列,无需回表
SELECT order_no, created_at FROM orders
WHERE status = 'PAID' AND created_at >= '2026-07-01';

规则6:ORDER BY字段纳入索引避免filesort

ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);

索引不是越多越好

每个额外索引的代价:INSERT/UPDATE/DELETE需要额外维护索引B+Tree;索引占磁盘空间(可能是数据量的30%-50%);Optimizer选错索引的概率增加。

生产环境经验:单表索引不超过8个,超过就审查哪些可以合并或删除。用sys.schema_unused_indexes找长期未使用的索引:

SELECT * FROM sys.schema_unused_indexes
WHERE object_schema = 'prod_db';

慢查询治理的长期机制

单次调优只能治标。长期治理靠三件事:

  1. 自动慢查询采集:pt-query-digest定时跑,结果写入监控平台,阈值告警
  2. SQL审核流程:DDL变更走工单,自动化EXPLAIN检查,发现全表扫描直接拦截
  3. 索引治理看板:重复索引、冗余索引、未使用索引按周出报告

数据库性能调优的核心不是”会写索引”,是”有体系地发现和消灭慢查询”。慢日志采集到pt-query-digest分析到EXPLAIN定位到索引优化到验证,这条流程跑通后,数据库性能问题就是可控的工程问题。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-man-cha-xun-zhen-duan-shi-zhan-cong-explain-fen-xi/

(0)
小编小编
上一篇 17小时前
下一篇 17小时前

相关推荐

发表回复

登录后才能评论