深度分页慢的关键在offset,mysql需扫描并丢弃前n行;推荐游标分页,即用上一页最后排序字段值(如id>1058)替代offset,并配合复合索引、覆盖索引、延迟关联等优化。

深度分页为什么慢?关键在 OFFSET
当执行 SELECT * FROM orders ORDER BY id LIMIT 100000, 20 时,MySQL 仍需扫描前 100000 行才能跳到目标位置。数据量越大,OFFSET 越大,性能越差——不是因为取 20 条慢,而是因为“扔掉”那 10 万行代价太高。
用主键/索引列替代 OFFSET:游标分页(推荐)
核心思路是:不依赖行号偏移,而记住上一页最后一条记录的排序字段值,下一页从该值之后开始查。
免费的开源程序长期以来,为中国的网上交易提供免费开源的网上商店系统一直是我们的初衷和努力奋斗的目标,希望大家一起把MvMmall网上商店系统的免费开源进行到底。2高效的执行效率由资深的开发团队设计,从系统架构,数据库优化,配以通过W3C验证的面页模板,全面提升页面显示速度和提高程序负载能力。3灵活的模板系统MvMmall网店系统程序代码与网页界面分离,灵活的模板方案,完全自定义模板,官方提供免费模
- 第一页:SELECT * FROM orders WHERE status = 1 ORDER BY id ASC LIMIT 20
- 第二页(假设第一页最大 id 是 1058):SELECT * FROM orders WHERE status = 1 AND id > 1058 ORDER BY id ASC LIMIT 20
- 必须确保 ORDER BY 字段有索引(如 INDEX(status, id)),否则无法高效定位
- 游标值(如 id > 1058)要由前端传回,不能用 OFFSET 计算,避免漏数或重复
其他实用优化手段
单靠游标还不够?再加几层保障:
- 覆盖索引:只查需要的字段,避免回表。例如 SELECT id, order_no, amount FROM orders …,配合 INDEX(status, id, order_no, amount)
- 延迟关联:大表 JOIN 时,先用游标分页获取主键,再用 IN 关联详情。比如先查出 20 个 id,再 SELECT * FROM orders WHERE id IN (…)
- 禁止跳页:前端禁用“跳转到第 100 页”按钮,只提供“下一页/上一页”,保证游标链连续
- 时间范围兜底:对按时间排序的场景(如 created_at),可用 WHERE created_at > '2024-01-01' AND ... ORDER BY created_at DESC LIMIT 20,比纯 ID 游标更符合业务直觉
什么情况仍需慎用游标?
游标分页不是银弹:
- 用户需要随机跳页(如搜索后翻到第 50 页),游标无法直接支持,可结合 ES 或预计算页码映射
- 排序字段存在大量重复值(如多个订单 created_at 相同),需增加二级排序字段(如 ORDER BY created_at DESC, id DESC)确保唯一性
- 数据实时写入频繁且排序字段更新多(如 status 频繁变更),可能导致游标查询结果错乱,此时应考虑物化视图或读写分离+一致性快照









