【MySQL】 索引、联合索引以及大数据量分批查询问题总结
MySQL 索引、联合索引以及大数据量分批查询问题总结
1. 索引到底是干什么的?
很多人理解索引:
加索引以后,查询就一定快。
这个理解是不准确的。
索引真正的作用:
帮助数据库快速定位数据,减少需要扫描的数据量。
例如一张表:
用户表
1000万条数据
查询:
SELECT *
FROM user
WHERE user_id = 10086;
如果没有索引,数据库只能:
第1条
第2条
第3条
...
第1000万条
一条一条判断。
这种叫:
全表扫描(Full Table Scan)
如果建立索引:
CREATE INDEX idx_user_id
ON user(user_id);
查询:
SELECT *
FROM user
WHERE user_id = 10086;
数据库可以:
索引
↓
快速定位 user_id=10086
↓
找到对应数据
不用扫描全部数据。
所以:
索引的本质,是减少扫描的数据量。
2. 索引是不是用了就一定快?
不是。
很多人的误区:
使用索引 = 一定快
实际上:
使用索引
↓
扫描数据少
↓
才会快
如果使用索引以后,仍然需要扫描大量数据,也可能很慢。
例如:
表:
订单表
1亿条数据
建立:
CREATE INDEX idx_create_time
ON order_table(create_time);
查询:
SELECT *
FROM order_table
WHERE create_time >= '2026-01-01';
执行计划:
type: range
表示:
走了索引范围查询
但是:
如果符合条件的数据:
5000万条
那么数据库仍然需要:
通过索引找到开始位置
↓
扫描5000万条数据
所以:
虽然:
走索引
但是:
扫描范围太大
仍然可能慢。
3. range 实际上是走了索引,对吧?
对。
例如:
SELECT *
FROM order_table
WHERE create_time >= '2026-07-01';
如果 create_time 有索引:
执行计划:
type: range
表示:
索引范围扫描
例如:
索引:
2026-01
2026-02
2026-03
...
2026-07
数据库:
找到2026-07的位置
↓
继续向后扫描
所以:
range = 走索引。
但是:
range 不代表一定快。
还要看扫描的数据量。
4. 普通索引和联合索引有什么区别?
4.1 普通索引
例如:
CREATE INDEX idx_name
ON user(name);
表示:
给一个字段建立索引。
查询:
SELECT *
FROM user
WHERE name='张三';
可以使用这个索引。
4.2 联合索引
例如:
CREATE INDEX idx_name_age
ON user(name,age);
表示:
多个字段组成一个索引。
例如:
数据:
张三 18
张三 20
李四 25
王五 30
索引结构类似:
name
|
+-- age
联合索引遵循:
最左匹配原则
索引:
(name,age)
可以支持:
WHERE name='张三'
也可以支持:
WHERE name='张三'
AND age=18
但是:
WHERE age=18
通常不能有效使用。
原因:
联合索引首先按照:
name
排序。
没有 name,数据库不知道从哪里开始查 age。
5. 为什么增加 id > 0 后扫描量变多?
假设:
表:
业务数据表
100万条数据
其中:
id 是主键
数据:
id
1
2
3
4
...
1000000
查询:
SELECT *
FROM A
WHERE
status='完成'
ORDER BY id
LIMIT 1000;
可能执行:
根据status索引找到符合数据
↓
排序id
↓
返回1000条
现在增加:
AND id > 0
变成:
SELECT *
FROM A
WHERE
status='完成'
AND id > 0
ORDER BY id
LIMIT 1000;
问题:
id > 0
对于主键来说:
基本等于:
所有数据
过滤效果很低。
但是同时:
存在:
ORDER BY id
LIMIT 1000
而:
主键天然按照id排序
所以 MySQL 可能认为:
直接扫描主键
不用额外排序
找到1000条停止
于是执行:
PRIMARY KEY扫描
id=1
判断条件
id=2
判断条件
id=3
判断条件
...
找到1000条
6. 为什么主键扫描反而慢?
例如:
总数据:
100万条
符合条件:
5000条
如果走业务索引:
先找到符合条件的数据
扫描5000条
如果走主键:
id=1
不符合
id=2
不符合
id=3
不符合
...
扫描20万条
才找到1000条
虽然:
使用了索引
但是:
扫描更多数据
所以仍然慢。
7. LIMIT 到底是什么执行逻辑?
例如:
表:
1000条数据
符合条件:
200条
SQL:
SELECT *
FROM A
WHERE 条件
LIMIT 100;
是不是:
先查询100条
然后过滤?
不是。
逻辑顺序:
FROM
↓
WHERE过滤
↓
ORDER BY排序
↓
LIMIT限制
实际逻辑:
1000条数据
↓
过滤
↓
剩余200条
↓
取前100条
但是实际执行时:
数据库可能提前停止。
例如:
扫描数据
找到第1条符合
找到第2条符合
...
找到第100条符合
停止
因为已经满足 LIMIT。
8. 大数据量为什么需要分批查询?
假设:
5000万条数据
一次查询:
SELECT *
FROM A;
问题:
- 内存压力大
- 查询时间长
- 数据处理慢
所以需要:
每次处理1000条
9. 为什么使用 id > 上一次ID?
不要使用:
LIMIT 100000,1000
因为:
页数越大:
扫描越多。
例如:
第一页:
LIMIT 0,1000
扫描:
1000条
第10000页:
LIMIT 10000000,1000
数据库需要:
扫描10001000条
丢弃前10000000条
返回1000条
推荐:
游标分页:
第一次:
SELECT *
FROM A
WHERE 条件
ORDER BY id
LIMIT 1000;
得到:
最后id=5000
第二次:
SELECT *
FROM A
WHERE 条件
AND id > 5000
ORDER BY id
LIMIT 1000;
继续。
10. 那 id > 0 这种方式有什么问题?
分页思想没有问题。
问题是:
第一次:
id > 0
范围太大。
同时:
ORDER BY id
容易让 MySQL 选择:
主键扫描
而不是:
业务条件索引过滤
正确方式:
后续:
id > 上一次最大id
是合理的。
11. 联合索引如何帮助分页查询?
假设:
SQL:
SELECT *
FROM A
WHERE
status='完成'
AND type='运输'
AND id > ?
ORDER BY id
LIMIT 1000;
如果只有:
status索引
数据库可能:
先过滤status
↓
再过滤type
↓
再排序id
效率一般。
可以建立:
CREATE INDEX idx_status_type_id
ON A
(
status,
type,
id
);
这样:
索引顺序:
status
↓
type
↓
id
数据库可以:
先过滤status
↓
过滤type
↓
按照id范围扫描
↓
返回1000条
12. 如何判断是不是索引选择错误?
使用:
EXPLAIN
SELECT ...
重点看:
key
实际使用的索引。
例如:
key: PRIMARY
表示走主键。
可能导致扫描大量数据。
rows
预计扫描行数。
例如:
rows: 500000
表示需要扫描很多数据。
如果:
rows: 5000
通常更合理。
Extra
例如:
Using filesort
表示额外排序。
但是:
不要简单认为:
没有filesort一定更快
因为:
为了避免排序走主键,有可能扫描更多数据。
13. 总结
核心理解:
1. 索引
作用:
减少扫描的数据量
不是:
用了索引一定快
2. range
表示:
索引范围查询
但是:
范围太大一样慢
3. 联合索引
作用:
多个字段一起建立索引
遵循:
最左匹配原则
4. 大数据分页
推荐:
id > lastId
ORDER BY id
LIMIT 1000
5. SQL 优化重点
不要只看:
有没有走索引
重点看:
扫描多少数据
使用什么索引
执行计划是否合理
最终目标:
让数据库尽可能少扫描数据。
更多推荐
所有评论(0)