首页/数据/数据库速查/LIMIT 分页与 OFFSET 深翻页问题
SQL 语法52 条

LIMIT 分页与 OFFSET 深翻页问题

限制返回行数并支持偏移;大偏移(LIMIT 1000000, 20)会扫描大量行,应改用基于游标的键集分页。

数据库速查数据更新于 2026-10-13

LIMIT 是什么

LIMIT 用来限制返回行数并指定偏移,有两种写法:LIMIT count 和 LIMIT offset, count(也可写成 LIMIT count OFFSET offset)。它作用在排序之后,是分页查询的最后一步。真正的坑在深翻页:LIMIT 1000000, 20 并不是只读二十行,MySQL 必须先扫描并丢弃前一百万行,代价随偏移线性上升。键集分页(记住上一页最后一条的游标值,用 WHERE id > ? 继续取)和延迟关联是两种常用替代方案。

基本信息

用途限制返回行数与分页
语法LIMIT count | LIMIT offset, count | LIMIT count OFFSET offset
执行位置排序之后、返回之前
深翻页问题偏移越大越慢,扫过的行数与偏移成正比
替代方案键集分页(游标)、延迟关联(先取主键再回表)
配套要求分页必须搭配确定性的 ORDER BY

典型使用场景

  • 列表接口分页,前端传入页码与每页条数
  • 运维排查时只看前若干行,避免把大结果集拉回本地
  • TopN 统计(最大金额、最慢语句),排序后取前 N 条
  • 批量任务分段处理:每次取一批主键,处理完再取下一批
  • 深翻页改造现场:把偏移分页换成游标式分页
  • 大表导出时按主键范围分批拉取,避免一次性加载

基础分页

基础分页
SELECT id, sku FROM orders ORDER BY id LIMIT 20;   # 第一页
SELECT id, sku FROM orders ORDER BY id LIMIT 20 OFFSET 20;   # 第二页,等价 LIMIT 20, 20
SELECT id, sku FROM orders ORDER BY id DESC LIMIT 10;   # 最新十条,反向扫描主键
SELECT COUNT(*) FROM orders WHERE status='paid';   # 列表页常要的配套总数,大表宜用估算

深翻页优化

深翻页优化
# 键集分页:记住上一页最后一条的 id,从它之后继续取
SELECT id, sku FROM orders WHERE id > 100020 ORDER BY id LIMIT 20;
# 延迟关联:先在索引上取出主键,再回表拿需要的列
SELECT o.id, o.sku FROM orders o
JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON t.id = o.id;   # 必须保留页码时用这种折中写法

分页写法对照

写法代价适用场景
LIMIT 20扫到满足二十行即停首页与结果集小的查询,成本最低
LIMIT 20 OFFSET 20读取并丢弃前二十行前几页,业务上通常够用
LIMIT 1000000, 20读取并丢弃一百万行深翻页,迟早要改造
WHERE id > 游标直接从索引位置起读深翻页首选,要求排序键唯一且单调
延迟关联先取主键再回表必须保留跳页能力时的折中方案
无 ORDER BY 的 LIMIT顺序不保证只适合抽样查看,不做业务用途
没有 ORDER BY 的 LIMIT 返回顺序不保证稳定,重复执行可能得到不同结果;分页、导出、对账这类场景必须显式排序。
OFFSET 的成本在于读了再丢,与返回行数无关。若排序键唯一且单调,键集分页可以直接从索引定位起读位置,耗时基本恒定,这是深翻页改造的首选思路。
带 LIMIT 的 UPDATE 或 DELETE 语义容易被误解:命中的是排序后的前若干行,排序不确定时命中的行也会变,写操作前先用相同条件 SELECT 计数核对。

分页可用性检查

  • 排序键是否唯一且单调:不满足就先补唯一列,再考虑键集分页
  • 首屏是否真的需要总数:多数列表页可用估算或滚动加载省掉计数
  • 是否有并发写入:有插入时偏移分页会出现条目漂移,键集分页更稳定
  • 每页条数是否过小:几十条一页会让请求次数成倍上升
  • 是否必须支持随机跳页:强跳页场景保留偏移分页并限制最大页码,其余改游标
  • 批量任务每批取完后是否记录游标:不记录就无法在中断后续跑
偏移分页在数据持续插入时会出现条目漂移:翻页过程中新记录插到了前面,第二页可能重复展示第一页见过的内容,业务上要么用游标分页,要么按固定时间快照查询。

易错点

  • 不写 ORDER BY 就分页:返回顺序不保证,用户翻页会看到重复或遗漏的记录
  • 深翻页继续用大偏移:扫描量随偏移线性增长,越翻越慢,最终拖垮实例,应改为键集分页
  • 排序键不唯一时做键集分页:游标比较会漏掉并列行,必须选唯一且单调递增的列作为游标
  • 分页每次都对大表执行全量计数:计数比取数据更贵,列表页通常改为估算或提供近似总数
  • 批量任务用 LIMIT 循环却不固定排序键:每批取到的行可能重叠或跳行,必须先确定排序规则

数据来源与参考

  • MySQL 8.0 Reference Manual: LIMIT Query Optimization

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌