覆盖索引不能直接加速limit 1000000,10,但可避免全表扫描和回表,是深分页优化前提;其生效需满足三条件:索引含order by列且顺序一致、含所有where列、查询字段全在索引中。

覆盖索引本身不能直接让 LIMIT 1000000, 10 变快,但它能让你“绕过”全表扫描和回表,是所有深分页优化的前提条件。
为什么只加索引还不够?
很多人建了 INDEX(idx_update_time) 就以为够了,结果 EXPLAIN 显示 rows 依然上百万、Extra 里还有 Using filesort 或 Using temporary。这是因为:
- 单列索引只能加速
WHERE过滤,但ORDER BY update_time LIMIT 1000000, 10还要排序+跳行,MySQL 得把所有匹配的行捞出来再排序 -
SELECT *触发回表:索引树上只有update_time和主键id,查字段得反复回到聚簇索引取完整行,IO 爆涨 - 即使有
ORDER BY id,如果WHERE条件没走主键或联合索引最左前缀,MySQL 仍可能放弃索引做全表扫描
覆盖索引必须满足三个硬条件
不是随便建个索引就叫“覆盖”,它得让 MySQL 仅靠索引就能拿到所有需要的数据(包括排序依据):
- 索引字段必须包含
ORDER BY的列,且顺序一致(如ORDER BY id→ 索引需以id开头) - 索引字段必须包含所有
WHERE条件列(如WHERE update_time > '2025-01-01'→ 索引里要有update_time) - 查询字段必须全部落在索引列中(即不能写
SELECT *,而要明确写SELECT id, name, update_time)
例如:表 account 上建联合索引 INDEX idx_cover (update_time, id, name),对应查询就得写成:
SELECT id, name, update_time FROM account WHERE update_time > '2025-01-01' ORDER BY update_time, id LIMIT 1000000, 10;否则 MySQL 无法用上覆盖索引。
覆盖索引 + 子查询才是标准组合拳
单纯靠覆盖索引查全量字段仍会慢——因为你要的仍是第 100 万条之后的 10 条,MySQL 还得在索引里扫 1000010 行。所以真正有效的做法是:
- 子查询只查
id(确保走主键或紧凑联合索引),快速定位目标 ID 列表 - 外层用这些
id去主键索引精准回表,避免无效扫描 - WHERE 条件必须下推到子查询里,否则外层 JOIN 会放大结果集
典型写法:
SELECT a.* FROM account a INNER JOIN ( SELECT id FROM account WHERE update_time > '2025-01-01' ORDER BY id LIMIT 1000000, 10 ) b ON a.id = b.id;注意:子查询里
ORDER BY id 必须有索引支撑,且 id 是主键或至少是索引最左列;否则子查询本身就会慢。最容易被忽略的坑:ORDER BY 字段没索引或顺序错
这是线上最常踩的坑——你以为加了索引就万事大吉,结果 EXPLAIN 显示 type=ALL 或 Extra=Using filesort。比如:
- 写
ORDER BY update_time DESC,但索引是INDEX(update_time)(默认 ASC),MySQL 无法复用索引排序 - 写
WHERE status = 1 AND update_time > '2025-01-01' ORDER BY id,但索引是INDEX(status, update_time),不包含id,排序仍要临时文件 - 复合索引顺序写反了:想要
WHERE a = ? AND b > ? ORDER BY c,却建了INDEX(b, a, c),导致索引失效
真正起效的索引必须严格匹配查询模式,尤其 ORDER BY 列要出现在索引最右端且方向一致。否则覆盖索引就是纸糊的。











