MySQL深分页变慢是因为OFFSET越大,需顺序扫描并丢弃前M行,时间复杂度O(M+N);优化应改用游标分页(WHERE排序字段>上一页末值)或延迟关联(子查询取ID再JOIN),并确保复合索引覆盖WHERE与ORDER BY字段。

LIMIT 和 OFFSET 能实现分页,但“高效”只在小数据量或靠前页码时成立;一旦 OFFSET 值超过几万,查询就会明显变慢——这不是写法问题,是 MySQL 扫描机制决定的。
为什么 OFFSET 越大越慢?
MySQL 执行 LIMIT N OFFSET M 时,必须先完整扫描、排序(如果有 ORDER BY)、并跳过前 M 行,再取 N 行。它不会“直接跳到第 M+1 行”,而是逐行计数跳过。
常见错误现象:
- 第 100 页(
OFFSET 990)还能接受,但第 10000 页(OFFSET 99990)响应超时或拖垮数据库 - 执行
EXPLAIN显示rows字段远大于你实际要的LIMIT数,说明扫描量过大
关键点:性能瓶颈不在 LIMIT,而在 OFFSET。哪怕你只取 1 行,OFFSET 500000 仍需跳过 50 万行。
怎么写才不容易翻车?
基础写法本身没问题,但必须配合确定性排序和合理索引,否则结果可能错乱或不可复现。
-
ORDER BY必须包含至少一个唯一列(如id),避免同值字段(如仅ORDER BY status)导致顺序不一致 - 复合排序更稳妥:例如
ORDER BY created_at DESC, id DESC,确保每行都有唯一位置 - WHERE 条件字段 + 排序字段必须联合建索引,比如
WHERE deleted = 0 ORDER BY created_at DESC, id DESC,对应索引应为(deleted, created_at, id) - 避免在大表上用
LIMIT 20 OFFSET 100000这类语句直连业务接口;前端“跳转到第 N 页”的输入框,后端应做校验或降级
什么时候该换方案?
当你的业务出现以下任一情况,LIMIT/OFFSET 就不该再是首选:
- 用户可随意跳转到高页码(比如后台导出功能要拉取第 5000 页)
- 单表数据量 > 100 万,且分页深度常超 1000 页
- 监控发现慢查询日志里反复出现
OFFSET >= 50000的语句
此时应切换到游标分页(Cursor-based Pagination):用上一页最后一条记录的排序字段值作为下一页起点,例如 WHERE created_at < '2026-08-20 10:30:00' AND id < 123456 ORDER BY created_at DESC, id DESC LIMIT 20。它不依赖偏移量,性能稳定,但要求前端保存游标状态,且不支持“跳转任意页”。
容易被忽略的细节
很多人以为把 LIMIT 10 OFFSET 20 改成 LIMIT 20, 10 就能优化,其实二者执行计划完全一样——这只是语法糖,MySQL 内部都转成相同逻辑。
另一个隐形坑:OFFSET 值不能是变量表达式(如 OFFSET @page * 20),必须是常量或参数化查询中的绑定值;否则预编译可能失效,影响执行计划复用。
真正影响性能的,从来不是你写了哪一种 LIMIT 语法,而是你有没有让 MySQL 少扫描、少排序、少跳过。


















