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