📢 欢迎来到万事技术论坛!本站仅讨论合法编程技术话题,严禁外挂/作弊/黑产/盗版内容,违者封号。

精华深分页慢到无法忍受?试试游标分页

dba_zhou 进阶会员
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

这条要先扫出并丢弃 100 万行才返回 20 行,越往后翻越慢。

为什么

LIMIT offset, n 的语义是「跳过 offset 行」,MySQL 必须真的把前 offset 行读出来才能跳过,哪怕最后扔掉。offset 越大浪费越多。

方案一:延迟关联(改动最小)

先用覆盖索引只查 id,再回表:

SELECT o.* FROM orders o
INNER JOIN (
    SELECT id FROM orders ORDER BY id LIMIT 1000000, 20
) t ON o.id = t.id;

子查询只扫索引不回表,数据量小得多,通常能快几倍到几十倍。

方案二:游标分页(推荐)

记住上一页最后一行的排序值,下一页从它之后开始:

SELECT * FROM orders WHERE id > :last_id ORDER BY id LIMIT 20;

直接走主键索引定位,性能和页码无关,永远 O(20)。

按时间排序同理(注意用 (created_at, id) 联合索引,因为时间可能重复):

SELECT * FROM orders
WHERE (created_at, id) < (:last_time, :last_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;

两种分页怎么选

OFFSET 分页 游标分页
跳页(直接去第 50 页) 支持 不支持
深分页性能 差 恒定
数据变动时的稳定性 会漏/重 稳定
典型场景 后台管理表格 信息流、App 下拉加载

另外 OFFSET 分页在数据插入时会导致漏读或重复(整体后移一行),信息流场景必须用游标。

想显示总条数怎么办

COUNT(*) 在大表上同样是全表扫。常见做法:

  • 只显示「下一页」按钮,不显示总数(信息流)
  • 用近似值:SHOW TABLE STATUS 的 Rows,或缓存一个定时更新的计数
  • 分页到一定深度就提示「结果过多,请缩小筛选条件」
楼主 · 2026-09-28 13:21 · 浏览 2
yuki 进阶会员

游标分页不能跳页这个缺点,我们的做法是:后台管理用延迟关联(要跳页),
C 端信息流用游标(只下滑),两套分开,各取所需。

1楼 · 2026-09-28 14:31
登录 后即可参与回复。