MySQL 深分页性能差的根源是 OFFSET 大时需扫描大量无用行,优化应采用基于主键 / 唯一索引的游标分页(如 WHERE id > 125 ORDER BY id LIMIT 20)或覆盖索引 + 延迟关联,确保 ORDER BY 字段有合适索引,避免函数、跨索引排序及无索引字段分页。

MySQL 分页查询性能差,核心问题不在 LIMIT 本身,而在于 OFFSET 过大时需扫描大量无用行。优化的关键是避免全表 / 全索引扫描跳过前 N 行,改用基于游标的“无状态分页”或覆盖索引 + 延迟关联。
用主键 / 唯一索引字段做条件分页(推荐)
替代 LIMIT 10000,20 这类深分页,改用上一页最后一条记录的主键值作为下一页起点:
- 第一页:
SELECT id,name,created_at FROM user ORDER BY id LIMIT 20 - 第二页(假设第一页最大 id=125):
SELECT id,name,created_at FROM user WHERE id > 125 ORDER BY id LIMIT 20 - 必须确保 ORDER BY 字段有索引(最好是主键或唯一索引),且 WHERE 条件能高效走索引
- 不支持直接跳转到第 100 页,但适合“下一页”场景,响应时间稳定在毫秒级
强制走覆盖索引 + 延迟关联
当必须支持任意页码跳转(如后台管理),且排序字段非主键时:
- 先用覆盖索引查出 ID(只查索引,不回表):
SELECT id FROM user ORDER BY created_at DESC LIMIT 10000,20 - 再用这些 ID 关联原表取完整数据:
SELECT u.* FROM user u INNER JOIN (SELECT id FROM user ORDER BY created_at DESC LIMIT 10000,20) t ON u.id = t.id - 要求 ORDER BY 字段上有联合索引(如
INDEX idx_created_id (created_at, id)),让子查询能走索引扫描 - 避免
SELECT *直接分页,减少 IO 和网络传输量
避免常见陷阱
这些写法会彻底失效索引或加剧性能问题:
- 在 ORDER BY 字段上使用函数:
ORDER BY UPPER(name)→ 索引失效 - WHERE 和 ORDER BY 跨不同索引字段:
WHERE status=1 ORDER BY created_at→ 可能触发 filesort - 未加索引的文本字段分页:
ORDER BY description→ 全表扫描不可避免 - 用 UUID 或随机字符串作主键分页:
ORDER BY uuid→ 索引物理顺序无意义,分页效率极低
其他实用建议
结合业务场景进一步压降开销: