深分页性能差因需扫描前N行;优化应改用游标分页,基于单调字段(如created_at)记录上页末值,下页查询WHERE created_at
深分页(比如
LIMIT 1000000, 20)在sql中性能急剧下降,本质是数据库仍需扫描前100万行才能定位到目标数据。优化核心不是“跳过”,而是“避免全量偏移扫描”。用游标分页替代 OFFSET
适用于按时间、ID等单调字段排序的场景。不依赖行号,而是记录上一页最后一条的排序值,下一页查询直接从该值之后取数据。
- 原写法(慢):
select * FROM orders ORDER BY created_at DESC LIMIT 100000, 20- 优化写法(快):
SELECT * FROM orders WHERE created_at- 关键:确保
created_at有索引;若存在相同值,需加唯一字段(如id)做二级排序和条件补充延迟关联减少回表成本
当分页字段和查询字段来自不同索引时,mysql可能先用索引查出ID,再回表取完整数据——深分页会让这个过程变得极重。
- 低效写法:
SELECT id, name, status FROM user WHERE status = 1 ORDER BY id LIMIT 100000, 20- 优化写法:
SELECT u.* FROM user u INNER JOIN (SELECT id FROM user WHERE status = 1 ORDER BY id LIMIT 100000, 20) t ON u.id = t.id- 原理:子查询只走索引(覆盖索引),拿到20个ID后,主表再精确回表,大幅降低I/O
物理分页 + 缓存预热
对访问规律强的分页(如热门榜单第1–100页),可提前计算并缓存结果。
- 后台定时任务生成各页的ID列表(如 redis 中存
rank:page:5→[1022, 1025, ...])- 用户请求第5页时,直接用这些ID批量查详情:
SELECT * FROM item WHERE id IN (1022,1025,...)- 适合读多写少、分页内容变化不频繁的业务,如商品排行榜、文章热榜
限制最大页码或改用搜索/筛选
技术优化之外,更应审视产品逻辑:用户真的需要翻到第5万页吗?
- 前端限制
OFFSET不超过 10000(即最多查到第500页,每页20条)- 提供搜索框、时间范围、分类筛选,帮用户快速定位,而不是靠盲目翻页
- 对管理后台等特殊场景,可启用“导出全部”代替深度浏览
不复杂但容易忽略:深分页问题往往在数据量突破百万后才暴露,上线前用真实数据量压测分页接口,比事后救火更有效。
