MySQL SQL调优详解:explain执行计划、索引失效、慢查询优化一条龙

一、MySQL架构

1.1 MySQL逻辑架构

复制代码
┌──────────────────────────────────────┐
│           客户端层(连接处理)         │
└─────────────────┬────────────────────┘
                  │
┌─────────────────┴────────────────────┐
│           Server层(SQL处理)        │
│  ┌─────────┐ ┌─────────┐ ┌────────┐ │
│  │ 连接器  │ │ 解析器  │ │ 优化器 │ │
│  └─────────┘ └─────────┘ └────────┘ │
└─────────────────┬────────────────────┘
                  │
┌─────────────────┴────────────────────┐
│           存储引擎层(InnoDB/MyISAM) │
└──────────────────────────────────────┘

1.2 InnoDB vs MyISAM

特性 InnoDB MyISAM
事务 支持 不支持
外键 支持 不支持
锁粒度 行锁 表锁
全文索引 5.6+支持 支持
崩溃恢复 支持 不支持

二、索引详解

2.1 索引数据结构

B+ Tree索引:

复制代码
        [15]                ← 索引页
       /    \
   [5,10]  [20,25]          ← 索引页
   / | \   / | \
  叶  叶  叶  叶  叶  叶      ← 数据页
  ↓  ↓  ↓  ↓  ↓  ↓
 实际数据,顺序链表连接

B+ Tree vs B Tree:

对比 B Tree B+ Tree
数据存储 所有节点 仅叶子节点
查询稳定性 不稳定 所有查询复杂度相同
范围查询 需要回旋 叶子节点链表支持

2.2 索引分类

类型 说明 示例
主键索引 主键自动建 PRIMARY KEY(id)
唯一索引 唯一不重复 UNIQUE(name)
普通索引 普通加速查找 INDEX(name)
联合索引 多列组合 INDEX(a,b,c)
全文索引 文本搜索 MATCH(content) AGAINST('关键词')

三、Explain执行计划

3.1 explain使用

sql 复制代码
EXPLAIN SELECT * FROM user WHERE name = '张三';

输出字段:

字段 含义
id 查询序号
select_type 查询类型
table 表名
type 访问类型
possible_keys 可用索引
key 实际使用索引
key_len 索引长度
ref 索引引用
rows 扫描行数估算
Extra 额外信息

3.2 type详解(从好到差)

type值 说明 案例
system 表只有一行 系统表
const 最多一行 PRIMARY KEY
eq_ref 唯一扫描 PRIMARY KEY/UNIQUE
ref 非唯一扫描 普通索引
range 范围扫描 BETWEEN/IN/LIKE
index 全索引扫描 INDEX
ALL 全表扫描 无索引

3.3 Extra详解

含义
Using filesort 需要额外排序(不好)
Using temporary 需要临时表(不好)
Using index 覆盖索引(好)
Using index condition 索引下推(好)
Using where 回表过滤

四、索引失效场景

4.1 索引失效的11种情况

sql 复制代码
-- 1. 索引列参与计算
EXPLAIN SELECT * FROM user WHERE age + 1 = 30;
-- ✖ 失效

-- 2. 索引列使用函数
EXPLAIN SELECT * FROM user WHERE SUBSTRING(name, 1, 3) = '张三';
-- ✖ 失效

-- 3. 类型转换
EXPLAIN SELECT * FROM user WHERE age = '30';
-- ✓ 有效(MySQL会自动转换)

EXPLAIN SELECT * FROM user WHERE name = 123;
-- ✖ 失效(字符串字段用数字查)

-- 4. LIKE以%开头
EXPLAIN SELECT * FROM user WHERE name LIKE '%三';
-- ✖ 失效

EXPLAIN SELECT * FROM user WHERE name LIKE '张%';
-- ✓ 有效

-- 5. OR前后不都是索引列
EXPLAIN SELECT * FROM user WHERE name = '张三' OR age = 30;
-- ✖ 失效(age不是索引)

-- 6. NOT IN / NOT EXISTS
EXPLAIN SELECT * FROM user WHERE age NOT IN (20, 30);
-- ✖ 可能失效

-- 7. != / <>
EXPLAIN SELECT * FROM user WHERE age != 30;
-- ✖ 可能失效

-- 8. 联合索引违反最左前缀原则
CREATE INDEX idx_name_age ON user(name, age);

EXPLAIN SELECT * FROM user WHERE name = '张三';
-- ✓ 有效

EXPLAIN SELECT * FROM user WHERE age = 30;
-- ✖ 失效

EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 30;
-- ✓ 有效

-- 9. 排序时索引失效
CREATE INDEX idx_name_age ON user(name, age);

EXPLAIN SELECT * FROM user ORDER BY name;
-- ✓ 有效

EXPLAIN SELECT * FROM user ORDER BY age;
-- ✖ 失效

EXPLAIN SELECT * FROM user ORDER BY name, age;
-- ✓ 有效

五、慢查询优化

5.1 开启慢查询日志

sql 复制代码
-- 查看慢查询开关
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time%';

-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- 查看慢查询日志
SHOW GLOBAL STATUS LIKE 'Slow_queries%';

5.2 my.cnf配置

ini 复制代码
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

5.3 慢查询分析工具

bash 复制代码
# 使用mysqldumpslow分析
mysqldumpslow -t 10 /var/log/mysql/slow.log

5.4 慢查询优化案例

优化前:

sql 复制代码
SELECT * 
FROM orders o, users u, products p
WHERE o.user_id = u.id 
  AND o.product_id = p.id
  AND o.create_time > '2024-01-01'
ORDER BY o.create_time DESC
LIMIT 100;

优化步骤:

Step1:添加必要索引

sql 复制代码
ALTER TABLE orders ADD INDEX idx_create_time(create_time);
ALTER TABLE orders ADD INDEX idx_user_id(user_id);
ALTER TABLE orders ADD INDEX idx_product_id(product_id);

Step2:只查必要字段

sql 复制代码
SELECT o.id, o.amount, o.create_time,
       u.name as user_name,
       p.name as product_name
FROM orders o
INNER JOIN users u ON o.user_id = u.id
INNER JOIN products p ON o.product_id = p.id
WHERE o.create_time > '2024-01-01'
ORDER BY o.create_time DESC
LIMIT 100;

Step3:创建覆盖索引

sql 复制代码
CREATE INDEX idx_cover ON orders(create_time, user_id, product_id, id, amount);

六、分页优化

6.1 普通分页(大数据量会慢)

sql 复制代码
SELECT * FROM orders LIMIT 1000000, 10;
-- 偏移量越大越慢

6.2 优化分页(使用ID)

sql 复制代码
-- 第一页
SELECT * FROM orders ORDER BY id LIMIT 10;

-- 下一页:记住上一页最后一条ID
SELECT * FROM orders 
WHERE id > 1000000 
ORDER BY id 
LIMIT 10;
-- 利用主键索引,极快

七、SQL优化经验总结

7.1 常用优化规则

规则 说明
SELECT只查需要的字段 减少网络传输,使用覆盖索引
避免SELECT * 无法使用覆盖索引
批量插入替代循环单条 减少网络开销
合理使用LIMIT 分页查询要带上上一页ID
索引列不能参与运算 会导致索引失效
避免隐式类型转换 字符串字段不要用数字比较

7.2 常用优化案例

JOIN优化:

sql 复制代码
-- 优化前:小表驱动大表没做好
SELECT * FROM big_table t1 
LEFT JOIN small_table t2 ON t1.id = t2.t1_id;

-- 优化后:让小表驱动大表
SELECT * FROM small_table t2 
LEFT JOIN big_table t1 ON t1.id = t2.t1_id;

IN优化:

sql 复制代码
-- 优化前:IN中数据量大
SELECT * FROM user WHERE id IN (1,2,3,...,10000);
-- 可能变成嵌套查询

-- 优化后:先查出来再JOIN
SELECT t1.* FROM user t1 
INNER JOIN (SELECT id FROM user LIMIT 10000) t2 
ON t1.id = t2.id;

总结

知识点 说明
执行计划 type/Extra/Key是核心
索引失效 最左前缀/函数/OR/LIKE%开头
慢查询 开启慢日志+explain分析
优化方向 索引+SQL+分页+JOIN顺序

核心口诀:EXPLAIN先行,索引失效要记清,SQL优化三原则, LIMIT分页要记牢。


相关推荐
KaMeidebaby17 分钟前
卡梅德生物技术快报|骆驼纳米抗体:从原核表达、高通量测序到分子对接全流程实现
前端·数据库·其他·百度·新浪微博
2301_783848653 小时前
优化文本分类中堆叠模型的网格搜索性能:避免训练卡顿的实战指南
jvm·数据库·python
拾贰_C3 小时前
【Ubuntu | 公共工作站 | mysql 】 MySQL残留物残留数据
linux·mysql·ubuntu
TE-茶叶蛋3 小时前
DBeaver 的Explain 执行计划,分析sql的性能
数据库·sql
CLX05054 小时前
如何安装Oracle 12c Cloud Control_OMS服务端组件与Agent部署
jvm·数据库·python
m0_617493944 小时前
PySide6 网络请求深度实测:从基础 API 调用到数据解析实战指南
数据库
知识汲取者5 小时前
每日一篇高频面试题系列之【MySQL 锁】
数据库·mysql
老纪5 小时前
SQL中如何查找特定的空值行:WHERE IS NULL深度解析
jvm·数据库·python
麦聪聊数据5 小时前
数据 API 平台选型:深度解读数据服务的四大关键技术与架构底座
数据库·sql
IT研究所5 小时前
AI 时代下的知识管理:从 Claude 的“复盘”能力看生成式 AI价值
大数据·运维·数据库·人工智能·科技·低代码·自然语言处理