Appearance
MySQL 分页优化:为什么 limit 越翻越慢,游标分页和覆盖索引怎么用
分页查询一开始通常都很好写:
sql
select * from table order by id limit 0, 20;但页数一深,问题就来了:
- 第 1 页很快
- 第 1000 页开始明显变慢
这不是 MySQL “突然不行了”,而是分页方式本身的成本在放大。
先说结论
深分页慢的根本原因通常是:
- MySQL 需要先扫描并跳过大量前置记录
常见优化方向有三种:
- 覆盖索引 + 回表优化
- 延迟关联
- 基于游标 / 上次位置的翻页
一、为什么 limit offset, size 会越来越慢
例如:
sql
limit 100000, 20这并不是“直接跳到第 100001 条”,而通常意味着:
- 先定位并扫描前面大量记录
- 再返回后面的 20 条
如果排序和过滤成本本来就不小,深分页会非常吃亏。
二、什么场景尤其容易慢
1. 大偏移量
页码越深,扫描和跳过的成本越高。
2. 还带复杂排序
如果排序字段没设计好索引,代价会进一步放大。
3. 一次查很多列
回表和数据传输成本也会叠加。
三、覆盖索引为什么是优化重点
如果分页先只查索引里能拿到的列,例如主键:
sql
select id from table where ... order by create_time limit 100000, 20;然后再根据这批 id 回表拿完整数据,往往会更划算。
因为第一页扫描阶段可以尽量走:
- 更轻的索引访问路径
四、延迟关联怎么理解
一种常见思路是:
- 先用索引查出当前页主键集合
- 再和主表关联拿完整数据
这本质上是在避免:
- 一开始就对大结果集做重回表
五、游标分页什么时候更合适
如果产品场景不需要“任意跳到第 N 页”,而更像:
- 下一页
- 再下一页
那么可以考虑基于上一次最后一条记录做翻页,例如:
sql
where id > last_id order by id limit 20这种方式最大的优点是:
- 不需要跳过大量历史记录
特别适合
- 时间线
- 消息流
- 后台滚动列表
六、分页优化更实用的思考顺序
1. 这个场景真的需要深分页吗
很多业务其实不需要允许用户跳到非常深的页数。
2. 排序字段有没有合适索引
如果没有,先做这一层。
3. 能不能拆成“先轻查主键,再回表”
这通常是很实用的优化。
4. 能不能改成游标式翻页
如果业务允许,这是更稳的方向。
一句话总结
MySQL 分页慢,不是 limit 语法的问题,而是深分页会天然放大扫描和跳过成本。
如果你能从“是否真的需要深分页、能否走覆盖索引、能否改成游标分页”这三层去优化,效果通常会比单纯调参数更直接。