LIMIT 越往后越慢是因为MySQL需扫描并暂存前offset+rows行再丢弃前offset行,导致I/O和CPU开销线性增长;覆盖索引须包含SELECT和ORDER BY所有字段且符合最左前缀原则;用WHERE id < 上一页最小id替代LIMIT offset可规避偏移成本。

为什么 LIMIT 越往后越慢?
MySQL 执行 LIMIT 10000, 20 时,不是跳过前 1 万行直接取 20 行,而是先扫描并暂存前 10020 行,再丢弃前 10000 行——这导致 I/O 和 CPU 开销随偏移量线性增长。尤其当 ORDER BY 字段没索引、或索引无法覆盖查询字段时,全表扫描或临时文件排序会进一步放大延迟。
覆盖索引怎么写才真正生效?
覆盖索引要求:查询的所有字段(包括 ORDER BY 和 SELECT 列)都必须包含在同一个联合索引中,且顺序要匹配最左前缀原则和排序需求。
- 错误写法:
INDEX (create_time)—— 只能加速排序,但查id和title还得回表 - 正确写法:
INDEX (create_time, id, title)—— 假设你按create_time DESC排序,并SELECT id, title - 注意:如果
WHERE条件存在(如status = 1),应把过滤字段放索引最左侧:INDEX (status, create_time, id, title)
用主键 ID 替代 OFFSET 的实际写法
适用于按主键或时间递增排序的场景,本质是把“跳过 N 行”转为“从某 ID 开始取”。避免 LIMIT 的偏移成本,但要求前端能记住上一页最后一条的 id 值。
- 原低效语句:
SELECT id, title FROM article ORDER BY id DESC LIMIT 10000, 20 - 优化后:
SELECT id, title FROM article WHERE id (假设上一页最大 <code>id是 123456) - 关键点:必须确保
id严格递增、无重复、不删除后复用;否则需用复合条件(如WHERE (create_time, id) )
哪些情况覆盖索引也救不了?
覆盖索引不是银弹。以下场景即使加了索引,性能仍可能卡在别处:
-
GROUP BY或DISTINCT导致临时表 + 文件排序,索引失效 - 查询字段含
TEXT/BLOB类型,MySQL 无法将其加入索引,强制回表 - 使用函数包裹排序字段,如
ORDER BY DATE(create_time),索引无法用于排序 - MySQL 版本低于 5.7 时,
JSON字段或虚拟列上的索引支持有限,覆盖效果打折扣
分页性能问题从来不是单点问题——索引设计、查询结构、数据分布、甚至客户端如何传参,都在同一条链路上。最容易被忽略的是:你以为加了索引就覆盖了全部字段,结果 EXPLAIN 里还躺着一个 Using filesort 或 Using temporary。



















