☰
MySQL 深分页为什么越翻越慢:LIMIT 大 offset 的三种优化方案
2026/10/14 14:53:08 网站建设 项目流程

后台订单列表,一开始翻着挺快。

后来运营说,翻到几百页以后特别卡,有时候直接转圈。

我打开慢查询日志,看见这么一条:

SELECT*FROMordersORDERBYidLIMIT40000,20;

这条 SQL 是 AI 写的。

offset 的代价是线性的

很多人对LIMIT offset, size的理解是“跳过 offset 行,取 size 行”。

数据库实际做的事是:

先按顺序读出 offset + size 行,然后把前 offset 行扔掉,返回剩下的。

LIMIT 40000, 20的意思不是“从第 40001 行开始读”,是“读 40020 行,扔掉 40000 行”。

所以你翻的页数越多,要读和要扔的行数越多。第 1 页读 20 行,第 1000 页读 20020 行,第 2000 页读 40020 行。

耗时基本和 offset 成正比。

注意这里的关键不是“扫描行数多”,而是这些行是有用的工作吗。不是。前面 40000 行读完就直接丢了,一点价值都没产生。

我拿一张三百万行的表试过,LIMIT 40000, 20和LIMIT 20的差距在几百毫秒量级。看着不多?但这一条查询占着连接、占着 CPU,几十个运营同时翻页,数据库就顶不住了。

为什么 AI 只会这一种写法

因为这是所有教程里的标准写法。

你搜“MySQL 分页”,前十条结果全是LIMIT offset, size。这个写法简单、通用、任何数据库都支持。教程要说的是“分页怎么做”,不是“分页怎么做才快”。

按 popularity 采样出来的结果,就是最普遍的写法,不是最优的写法。

解法一:游标分页(推荐)

不要用 offset,用“上一页最后一条的 id”来定位:

-- 第一页SELECT*FROMordersORDERBYidLIMIT20;-- 下一页:把上一页最后一条的 id 传进来SELECT*FROMordersWHEREid>40000ORDERBYidLIMIT20;

这样每次都是“从 id 大于某个值开始,取 20 条”,走索引直接定位,代价是常数级。

代价是:只能顺序翻页,不能跳页。上一页怎么处理也得想清楚(要么缓存,要么反向查)。

对“无限下拉加载”这种场景,这是最优解。对“必须能跳到第 500 页”的后台管理,就得考虑下一条。

解法二:延迟关联

如果必须保留 offset 跳页,用这个:

SELECTo.*FROMorders oINNERJOIN(SELECTidFROMordersORDERBYidLIMIT40000,20)AStONo.id=t.id;

原理:先在只查主键的子查询里做分页(主键索引是覆盖索引,扫描代价小),拿到 20 个 id 之后,再用主键回表取完整数据。

扫描行数还是那么多,但每行只读 id,不读所有字段,代价能降一大截。

注意这个方法的前提是有合适的主键或索引。如果排序字段本身没索引,那还是得全表扫,延迟关联救不了。

解法三:改产品设计

前两个都是技术解法。但还有个更直接的:

能不能别让用户翻这么深?

一个订单列表,翻到第 2000 页的用户,大概率不是“想看第 40000 条订单”,而是搜索条件没加。

后台列表正确的做法是逼用户加筛选条件:时间范围、状态、用户 ID,随便哪个都能把数据量降到几百条。翻页问题直接消失。

顺带说一个连带问题:COUNT

AI 写分页通常还会配一个总数查询:

SELECTCOUNT(*)FROMordersWHERE...;

COUNT(*)在 InnoDB 里没法走索引直接得到结果(因为 MVCC,每行可见性不同),得实际扫。数据量大的时候,这个查询能和分页查询一样慢。

优化方案:

  • 如果只是做 UI 上的“第 x / y 页”,用估算值就够(EXPLAIN里的 rows,或者维护一张统计表)
  • 如果非要精确值,那就接受它慢,或者异步算、缓存起来
  • 很多产品其实只需要“下一页”,根本不需要知道总数。先问产品这个数字给谁看。

最后一个提醒:分页插件也一样

很多人用 PageHelper 这类分页插件。要清楚它是怎么实现的:

它就是在你的 SQL 后面拼LIMIT offset, size。

你写pageNum=2000, pageSize=20,它拼的就是LIMIT 40000, 20。问题一模一样,只是你看不见那条 SQL 了。

所以用了插件反而更要小心。拼出来的 SQL 你平时不看了。

一句话

分页不是“取第几页”的问题,是“怎么不干无用功”的问题。

LIMIT 40000, 20干了 40000 行的无用功。能不用 offset 就不用。

你们项目里最深的分页是第几页?翻一下试试,看能不能复现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询