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分页要记牢。


相关推荐
guodingdingh11 小时前
软件开发工作问题总结0718
java·开发语言·数据库
GuWenyue13 小时前
Cursor黑盒拆解!1套LangChain.js手写Mini编程Agent,自动生成React项目,效率提升60%
前端·数据库·人工智能
Csvn14 小时前
📊 SQL 入门 Day 8:集合操作 — 用 SQL 做数学里的"并交差"
后端·sql
Miao1213115 小时前
某海外住宿平台如何在大规模场景下实现指标一致性:Minerva 指标平台实践
大数据·数据库·人工智能
CodexDave15 小时前
MySQL事务隔离级别与MVCC机制解析
前端·数据库·mysql·nginx·性能优化·负载均衡
碎光拾影15 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
曾阿伦15 小时前
MongoDB 查询语句备忘手册
数据库·mongodb
一叶龙洲15 小时前
wslg打开Ubuntu24.04默认打开图形界面
linux·服务器·数据库·ubuntu
CCPC不拿奖不改名15 小时前
大模型推理架构与开源生态知识整理
数据库·windows·python·架构·langchain·开源·github
爬也要爬着前进17 小时前
redis主从搭建
数据库·redis·缓存