你可以这样考虑! 不走索引肯定慢,所以ALL是最低级的。走非唯一索引得分三种情况,走完全部索引就是index,因为走完了,所以也慢。 Range是对索引列范围查询,只走了部分索引,所以会快一些。Ref是对索引列做等值查询,比Range还快。而Eq_Ref是走唯一索引的关联查询,因为关联所以有一些开销,但是因为是唯一,所以比Ref快,而Const是唯一索引做等值查询,而且还不关联,那就更快了。所以就速度来说:All<Index<Range<Ref<Eq_Ref<Const
一、type(访问类型)——数据查找方式
核心口诀:
"ALL 全扫,index 全索,range 范围,ref 索引查,eq_ref 唯一连,const 主键到"
1. ALL
- 场景:全表扫描(性能最差)。
- 出现条件:无索引或无法使用索引时。
- 例子:
SELECT * FROM users WHERE name = 'Alice'(name无索引)。
2. index
- 场景:全索引扫描(比
ALL快,但需遍历整个索引树)。 - 出现条件:需要读取索引的全部数据(如覆盖索引)。
- 例子:
SELECT id FROM users(id是主键索引)。
3. range
- 场景:索引范围扫描。
- 出现条件:使用
BETWEEN、IN、>、<等范围查询。 - 例子:
SELECT * FROM users WHERE age > 20(age有索引)。
4. ref
- 场景:普通索引查找(可能返回多行)。
- 出现条件:使用非唯一索引的等值查询。
- 例子:
SELECT * FROM users WHERE email = 'alice@example.com'(email是普通索引)。
5. eq_ref
- 场景:唯一索引关联(每行最多匹配一条记录)。
- 出现条件:主键或唯一索引的关联查询(如
JOIN)。 - 例子:
SELECT * FROM users JOIN orders ON users.id = orders.user_id(orders.user_id是唯一索引)。
6. const
- 场景:常量查询(性能最好)。
- 出现条件:通过主键或唯一索引直接定位单行。
- 例子:
SELECT * FROM users WHERE id = 1(id是主键)。
二、select_type(查询类型)——SQL 语句结构
核心口诀:
"SIMPLE 简单无子查,PRIMARY 外层顶呱呱,SUBQUERY WHERE 里,DERIVED FROM 子查它"
1. SIMPLE
- 场景:简单查询(无子查询或 UNION)。
- 例子:
SELECT * FROM users WHERE id = 1。
2. PRIMARY
- 场景:最外层的查询(复杂查询中的主查询)。
- 例子:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)。
3. SUBQUERY
- 场景:子查询位于
WHERE子句中。 - 例子:
SELECT * FROM users WHERE id = (SELECT user_id FROM orders LIMIT 1)。
4. DERIVED
- 场景:子查询位于
FROM子句(生成临时表)。 - 例子:
SELECT * FROM (SELECT * FROM users) AS tmp。
5. UNION
- 场景:UNION 中的第二个或后续查询。
- 例子:
SELECT * FROM users UNION SELECT * FROM admins。
6. UNION RESULT
- 场景:UNION 的结果集。
- 例子:同上 UNION 查询的结果。
三、Extra(附加信息)——执行细节
核心口诀:
"Using where 过滤行,Using index 覆盖爽,Using temporary 临时表,Using filesort 排序忙"
1. Using where
- 场景:需要从存储引擎读取数据后,在 Server 层进行过滤。
- 例子:
SELECT * FROM users WHERE age > 20(age无索引)。
2. Using index
- 场景:覆盖索引(直接从索引获取数据,无需回表)。
- 例子:
SELECT id FROM users(id是主键)。
3. Using temporary
- 场景:需要临时表处理查询(如
GROUP BY或DISTINCT)。 - 例子:
SELECT DISTINCT name FROM users(name无索引)。
4. Using filesort
- 场景:需要额外排序(可能用到磁盘或内存)。
- 例子:
SELECT * FROM users ORDER BY name(name无索引)。
5. Using join buffer
- 场景:关联查询时使用连接缓冲区(如未走索引的 JOIN)。
- 例子:
SELECT * FROM users JOIN orders ON users.name = orders.product(name和product无索引)。
四、记忆技巧总结
-
关联性能:
type的值按性能排序:const > eq_ref > ref > range > index > ALL。Extra中Using index是好的,Using temporary和Using filesort是警告信号。
-
场景联想:
type=ALL想象成翻遍整个抽屉找东西,type=const像直接打开抽屉的某个格子。
-
对比记忆:
refvseq_ref:ref是普通索引查多行,eq_ref是唯一索引查单行。SUBQUERYvsDERIVED:SUBQUERY在WHERE里,DERIVED在FROM里。
五、实战验证表
| 字段 | 关键值 | 常见场景 | 性能影响 |
|---|---|---|---|
| type | const | 主键或唯一索引的等值查询 | ⭐⭐⭐⭐⭐ |
eq_ref | 主键关联查询(如 JOIN) | ⭐⭐⭐⭐ | |
ref | 普通索引的等值查询 | ⭐⭐⭐ | |
range | 索引范围查询(如 BETWEEN) | ⭐⭐ | |
index | 全索引扫描 | ⭐ | |
ALL | 全表扫描 | 🚨 | |
| Extra | Using index | 覆盖索引 | ✅ |
Using where | Server 层过滤数据 | ⚠️ | |
Using filesort | 额外排序 | 🚨 | |
Using temporary | 使用临时表 | 🚨 |
通过结合口诀和场景,反复分析实际 EXPLAIN 结果,可以逐步内化这些概念!