订单列表滚到很后面就转圈,offset 越大越慢,能不能只把每页条数调小?
订单列表滚到后面越来越慢,通常不是每页条数设大了,而是 limit offset 要先把前面大量行扫出来再丢掉,offset 越大越慢。按 2026 年项目交付经验,订单流水、操作日志这类主要靠连续往下翻的列表,常见做法是改成基于有序且不重复字段的游标分页;但需要任意跳页、精确总页数的报表接口,不适合硬改,否则问题会从前端或统计口径冒出来。
为什么 offset 越大越慢,调小每页条数为什么救不了
先看语义。limit 100000, 20 不是直接跳到第 100000 行,而是从符合条件的第一行开始数,数到第 100000 行之后才取 20 行。即使查询用上索引,数据库也要沿着索引走大量行,再把前面的行丢弃。成本跟 offset 大小走,不跟每页条数走。
常见误区是「把 pageSize 从 50 改成 20,接口就会快」。每页条数只影响最后返回和网络传输,前面被扫描丢弃的行数几乎没变。经验区间:数据量几万行、翻页几十页以内,差异可能不明显;到几十万行、offset 到几万甚至十几万时,单次查询从几十毫秒涨到几百毫秒以上是常见现象,具体受硬件、索引覆盖、过滤条件和排序方式影响。
- 翻到第 N 页的耗时大致随 N 增长,不是随 pageSize 增长。
- 排序字段没有合适索引时,数据库还要额外排序,深翻页更明显。
- 列表带 status、tenant_id 等过滤条件时,回表次数和扫描行数会叠加。
在深翻页场景里,offset 是成本变量,不是普通分页参数;调小每页条数改的是最后一小段,改不了前面的大扫描。
游标分页和 limit offset 的差别在哪
游标分页不数前面的行,而是拿上一页最后一条记录的有序键值当起点。例如按 ID 倒序时写成 where id < 上一页最后一条 id order by id desc limit 20。数据库可以直接用索引定位到起点,再往后取一页,翻页深度对单次查询耗时影响通常小得多。
- 定位方式:offset 数行丢弃,游标按有序键直接定位。
- 翻页深度影响:offset 随页数增长,游标在排序稳定时基本平稳。
- 跳页能力:offset 支持跳到任意页码,游标通常只能上一页或下一页。
- 排序字段要求:offset 对排序字段要求低,游标必须有序且能保证不重复。
- 总数统计:offset 容易配套 count,游标要另算总数或只给是否还有下一页。
- 适用对象:offset 适合后台报表、需要页码的列表;游标适合订单流水、消息流、日志流。
游标分页不是更高级的分页,它只是把按位置翻页换成按值翻页,代价是失去跳页和精确页码。
哪些订单列表适合改游标分页,哪些不适合
判断标准不是接口有没有慢,而是业务是否需要任意跳页。如果运营主要连续往下看最近订单,跳到第 50 页的需求很少,深翻页就是可以省掉的成本。反过来,如果报表页必须直接跳页、还要显示共多少页,游标分页会把这些需求推给另一套逻辑,不一定划算。
适合改的情况:
- 按创建时间或 ID 倒序的流水列表,用户连续往下翻。
- 单页数据量偏大、翻页深度经常超过几十页。
- 排序字段不重复,或可用「时间 + ID」组合保证不重复。
- 前端是滚动加载或加载更多,不依赖页码跳转。
不适合改的情况:
- 需要直接跳到指定页码的运营报表。
- 必须展示精确总页数或总条数的场景。
- 排序字段可随意切换,且找不到稳定不重复键。
- 数据量很小、翻页很浅,经验区间是万级以内时,改造收益不明显。
如果业务不需要跳页,深翻页就是一笔可以砍掉的成本;如果需要跳页,硬改游标往往只是把问题挪到前端或统计口径。
落地游标分页要先核对哪些字段和口径
游标分页的代码量不大,难点在字段选择和接口契约。按交付习惯,可以先走一套四步核对法,把丢记录、重复记录和兼容问题提前挡住。
- 确定有序不重复键:单列自增 ID 比较省事;如果按创建时间排序,用 (create_time, id) 组合,避免同一时间多条记录被跳过或重复。
- 明确游标传递:接口返回 next_cursor 和 has_more,前端原样回传,不要让前端自己拼页码或加减。
- 处理边界:第一页不传游标;最后一页返回 has_more=false;反向翻页时比较符号要反过来。
- 保留兼容:旧 offset 接口并行一段时间,新老客户端分别对接,等覆盖率上来再下线。
这样划分的原因是:有序键决定不丢不重,游标传递决定前后端理解一致,边界决定最后一页体验,兼容决定发布风险。排序和索引行为可按所用数据库官方文档核对,不要凭感觉假设。
交付现场:只改一个流水列表的约束和代价
交付现场常见约束是「预算和周期只够改一个列表,运营又要求最近三个月订单按时间倒序连续翻」。2026 年常见做法是先只改订单流水接口,保留原 offset 接口给需要跳页的导出页。
可核对对比(经验区间,不是固定报价):只加索引或裁剪字段,常见投入约半天到 1 人日,浅翻页和带过滤条件时有帮助,深翻页收益有限;改造为游标分页,常见投入约 1 到 2 人日开发、半天联调,连续翻页耗时更平稳,但要补 has_more 和游标契约;导出链路另拆,常见投入约 2 到 3 人日,能减轻接口内存和超时压力,但周期更长。代价是导出页或跳页报表仍然慢,需要后续再拆。
游标分页的验收口径不是「接口不超时」,而是「翻到任意深度耗时稳定、不丢记录、不重复记录、老客户端不报错」。
常见问题
只把每页条数调小,能不能解决深翻页慢?
通常不能。每页条数只影响最后返回和网络传输,offset 前面要扫描丢弃的行数没变,深翻页成本仍在。
游标分页是不是一定要用自增 ID?
不是。自增 ID 只是比较省事的有序不重复键;用创建时间加 ID 组合也可以,关键是排序结果稳定且不重复。
改成游标分页以后还能显示总页数吗?
精确总页数要另做 count 或近似统计;深翻页场景下建议只显示「加载更多」,不要强行算总页数。
数据量不大也要改成游标分页吗?
不必。经验区间是万级以内、翻页深度浅时,limit offset 通常够用,改造反而增加联调成本。
老客户端还在用 page 参数怎么办?
可保留旧 offset 接口一段时间,新接口并行提供,等客户端覆盖率上来再下线,避免直接切换导致老版本报错。
行动指引:先拉线上深翻页请求的 offset 分布和 P95 耗时。如果多数请求集中在前几页,优先做索引和字段裁剪;只有翻页深度稳定超过几十页、且业务不需要跳页时,再按游标分页改造。适用边界:需要精确页码、任意跳页或排序不稳定的报表类接口,不建议强行改造,可继续用 offset 并单独优化导出链路。不适用边界:排序键重复、数据量小且翻页浅、接口契约暂时无法加游标字段时,先别硬改。
-
列表接口总是一次查几万条,前端又要分页又要合计,怎么给数据才不卡?
日期:2026年9月4日 阅读:126
-
活动一开始接口就被打满,QPS 从平时平稳冲到活动峰值,是不是只把限流配在网关就漏了?
日期:2026年9月28日 阅读:88
-
表上索引越加越多,批量导入从几分钟涨到十几分钟,是不是索引加多了?
日期:2026年9月27日 阅读:38
-
运营要一次导出十万行订单,接口老是内存爆掉,只能让他分批导吗?
日期:2026年9月26日 阅读:42
-
手机号加密后运营按号码查用户,只能全表解密再比对吗?
日期:2026年9月25日 阅读:45




