知用网
第二套高阶模板 · 更大气的阅读体验

解决limit分页在数据量大时卡顿的实用方法

发布时间:2026-01-17 21:51:04 阅读:230 次

做后台开发或者写接口的时候,用 LIMIT 做分页再常见不过了。比如 MySQL 里写 LIMIT 10, 20 拿第 2 页的数据。可一旦表里数据上百万,翻到后面几页,比如第 50000 页,页面卡得像老电脑开 PS,等好几秒才出结果。

为什么大数据量下 LIMIT 越往后越慢?

问题不在 LIMIT 本身,而在它的执行方式。MySQL 查 LIMIT 100000, 10 的时候,得先从头扫 100010 条记录,再扔掉前 100000 条,只留最后 10 条。这就像你让快递员从仓库第一箱开始数,数到第十万零一箱才给你拿货,前面十万箱白搬。

传统分页写法的问题

常见的写法:

SELECT * FROM orders ORDER BY id DESC LIMIT 100000, 10;

即使有索引,OFFSET 越大,扫描的行数越多,IO 和 CPU 消耗直线上升。用户多翻几页,数据库直接拖垮。

改用“游标分页”避开深翻

别再用 OFFSET,换成基于上一页最后一条数据的位置继续查。比如按 ID 递增排序,上一页最后一条的 ID 是 99990,那下一页就从大于这个值的地方开始拿:

SELECT * FROM orders WHERE id > 99990 ORDER BY id ASC LIMIT 10;

这样不用跳过任何数据,直接走索引查找,速度飞起。就像你知道快递在哪个货架,直接过去拿,不用从门口一路数过来。

适用于时间线类场景

如果你做的是订单列表、日志查询、朋友圈动态这类按时间展示的内容,完全可以拿时间戳当游标。比如上一页最后一条是 “2024-06-10 14:30:25”,下一页查:

SELECT * FROM logs WHERE create_time > '2024-06-10 14:30:25' ORDER BY create_time ASC LIMIT 10;

前提是 create_time 上有索引,并且能保证唯一性(或配合主键去重)。

前端怎么传?简单改造就行

原来前端传 page=50001,现在改成传 last_id=99990。后端判断有没有 last_id,没有就查第一页,有就按游标方式查。接口返回数据的同时,带上当前页最后一条的 ID,前端存着,点“下一页”时带上。

不能用游标?试试延迟关联

如果非得保留页码,又不想卡,可以用“延迟关联”优化。先只查主键,再关联原表拿数据:

SELECT o.* FROM orders o INNER JOIN (
    SELECT id FROM orders ORDER BY id DESC LIMIT 100000, 10
) AS tmp ON o.id = tmp.id;

子查询只走索引查 ID,速度快;外层再用这些 ID 回表取完整数据。虽然不如游标快,但比直接 LIMIT 100000,10 强太多。

小结:别让 LIMIT 成性能黑洞

数据量小的时候怎么写都行,量一大,LIMIT 后面跟个大数字就是隐患。能换游标就换,不能换就用延迟关联顶一阵。别等到用户投诉“翻个页要等十秒”,才想起来查数据库。