MySQL分页查询优化核心是减少无效扫描,主要方法包括:用主键条件替代OFFSET、覆盖索引+延迟关联、限制页码或改用游标分页、合理设计复合索引避免filesort。

MySQL 分页查询在数据量大时容易变慢,核心问题在于 OFFSET 越大,扫描行数越多。优化不是单纯加索引,而是要减少无效扫描、避免全表跳过、改写查询逻辑。
用主键/唯一索引替代 OFFSET
当按主键升序分页时(如 ORDER BY id ASC),可把 LIMIT offset, size 改为基于上一页最大 ID 的条件查询:
- 第1页:
SELECT * FROM orders ORDER BY id LIMIT 20 - 第2页(已知上页最大 id=105):
SELECT * FROM orders WHERE id > 105 ORDER BY id LIMIT 20
这样跳过前 N 行的代价从 O(N) 降到 O(1),尤其适合“下一页”场景;但不适用于随机跳页(如直接跳到第1000页)。
覆盖索引 + 延迟关联(适用于复杂查询)
如果分页字段和查询字段不同(例如按 create_time 排序,但需返回 user_name、amount 等),可先查主键再关联:
- 低效写法:
SELECT id, user_name, amount FROM orders ORDER BY create_time DESC LIMIT 10000, 20(需排序+回表+跳过10000行) - 优化写法:
SELECT t1.id, t1.user_name, t1.amount FROM orders t1 INNER JOIN (SELECT id FROM orders ORDER BY create_time DESC LIMIT 10000, 20) t2 ON t1.id = t2.id
子查询只走索引(create_time + id 联合索引),主表仅回表 20 次,大幅减少 I/O 和内存开销。
限制可访问页码或改用游标分页
业务上很少真需要翻到第10万页,可主动约束:
- 前端禁用“跳转到页码”输入框,只提供“上一页 / 下一页”按钮
- 后端校验
page <= 200,超限返回空或提示“已到末尾” - 高并发列表(如消息流、订单流)推荐游标分页:客户端传上次最后的
create_time和id,服务端查WHERE create_time < ? OR (create_time = ? AND id < ?) ORDER BY create_time DESC, id DESC LIMIT 20
游标分页无偏移成本,结果稳定(避免新数据插入导致翻页错位),适合实时性要求高的场景。
合理设计索引,避免 filesort
分页性能差常因排序无法走索引。确保 ORDER BY 字段有高效索引:
- 单字段排序:建对应字段的索引,如
INDEX idx_status_ctime (status, create_time)支持WHERE status=1 ORDER BY create_time DESC - 避免在
ORDER BY中使用函数、表达式或不同方向混合(如ORDER BY a ASC, b DESC在 MySQL 8.0 前可能失效) - 用
EXPLAIN检查type是否为range或index,Extra不含Using filesort
索引不是越多越好,重点覆盖高频分页查询的 WHERE + ORDER BY + SELECT 组合。
不复杂但容易忽略:分页慢往往不是数据库不行,而是查询方式没对齐数据分布和索引能力。











