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→ 索引物理顺序无意义,分页效率极低
其他实用建议
结合业务场景进一步压降开销:
- 前端限制最大可查看页码(如只允许查前100页),后端对超出页码返回空或提示
- 对实时性要求不高的列表,用Elasticsearch或物化视图预计算分页结果
- 监控慢查询日志,重点抓
Rows_examined远大于Rows_sent的分页SQL - MySQL 8.0+可尝试
WINDOW FUNCTION配合ROW_NUMBER(),但大数据量下仍不如游标方案高效

免函数、跨索引排序及无索引字段分页。






