数据库 limit(大数据量下的分页福音:为什么用了这种LIMIT写法,查询快了100倍)

数据库 limit(大数据量下的分页福音:为什么用了这种LIMIT写法,查询快了100倍)
大数据量下的分页福音:为什么用了这种LIMIT写法,查询快了100倍

被OFFSET拖垮的数据库,用这个技巧3秒变30毫秒

作为一个整天和数据库打交道的资深开发,我最常听到的抱怨就是:“系统跑着跑着,分页查询越来越慢,用户翻到100页直接超时”。

如果你也遇到这种情况,问题十有八九出在你写的LIMIT上

一、OFFSET是个大坑:越往后翻越慢

先看一段最熟悉的分页查询:

SELECT * FROM orders ORDER BY create_time DESC LIMIT 10 OFFSET 100000;

这条SQL的逻辑是:先查100010条,再扔掉前100000条,返回最后10条

我在一张1000万行的订单表上做过实测,结果触目惊心 :

偏移量

耗时

状态

OFFSET 100

1ms

✅ 正常

OFFSET 10000

120ms

⚠️ 警告

数据库 limit(大数据量下的分页福音:为什么用了这种LIMIT写法,查询快了100倍)

OFFSET 100000

1.8s

❌ 不可接受

OFFSET 1000000

12s+

系统崩了

原因很简单:MySQL需要扫描全部100万行数据,然后才给你最后10条 。越到后面的页面,扫描的数据越多,速度自然越慢。

二、高手怎么写分页?用“游标”代替“页码”

既然OFFSET的问题在于“扫描太多”,那我们就换一种思路:不跳过,直接定位

这种写法叫做“游标分页”或“Keyset Pagination” :

-- 第一页:正常查SELECT * FROM orders WHERE user_id = 100 ORDER BY id DESC LIMIT 10;-- 下一页:记住上一页的最后一条idSELECT * FROM orders WHERE user_id = 100 AND id < 上一页最后一条idORDER BY id DESC LIMIT 10;

核心原理:利用索引直接定位,每次查询只扫描需要的10条数据,和页码无关

三、实战案例:从70秒到0.5秒

上个月帮一个电商系统优化订单分页,原SQL长这样:

-- 查第9万页,耗时70秒!SELECT * FROM order_detail WHERE status = 1 ORDER BY id LIMIT 900000, 10;

执行计划显示:扫描90万行,Using filesort 。

优化后的SQL:

-- 先查ID,再关联回表SELECT t.* FROM order_detail tINNER JOIN (    SELECT id FROM order_detail     WHERE status = 1     ORDER BY id     LIMIT 900000, 10) tmp ON t.id = tmp.id;

耗时从70秒降到了0.47秒

如果再配合游标写法,效果更夸张:

-- 记住上一页的最大idSELECT * FROM order_detail WHERE status = 1 AND id > 上一页最大idORDER BY id LIMIT 10;

耗时8毫秒,比原来快了将近10000倍

四、多个字段排序怎么办?

很多业务需要按时间排序,但时间可能重复。这时候需要用复合游标

-- 第一页SELECT * FROM orders WHERE user_id = 100 ORDER BY create_time DESC, id DESC LIMIT 10;-- 下一页(假设上一页最后一条 create_time='2024-01-01 10:00:00', id=9527)SELECT * FROM orders WHERE user_id = 100 AND (create_time < '2024-01-01 10:00:00'      OR (create_time = '2024-01-01 10:00:00' AND id < 9527))ORDER BY create_time DESC, id DESC LIMIT 10;

这种写法完美利用联合索引,性能依然是毫秒级 。

五、还有一种更优雅的写法:延迟关联

如果业务必须保留跳页功能(比如必须能直接跳到第100页),可以用“延迟关联 + 覆盖索引” :

SELECT * FROM orders tINNER JOIN (    SELECT id FROM orders     WHERE user_id = 100     ORDER BY create_time DESC     LIMIT 90000, 10) tmp ON t.id = tmp.id;

子查询只在索引上扫描,不回表;主查询用主键快速关联。实测1000万数据下,从12秒降到0.8秒

六、什么时候不能用游标分页?

游标分页有一个致命缺点不能跳页

  • ✅ 适合:App下拉加载更多、朋友圈滚动、消息列表
  • ❌ 不适合:后台管理系统需要直接跳到第100页

如果你必须支持跳页,就用“延迟关联”方案,至少能优化80%的性能。

写在最后

OFFSET就像年轻时犯的错,迟早要还的。

如果你的数据量已经超过百万,分页查询越来越慢,不要再加索引了——换一种写法,比加100个索引都管用

今日行动

  1. 打开你的慢查询日志,找到最慢的分页SQL
  2. 换成游标分页或延迟关联
  3. 感受数据库CPU从100%降到5%的快感

评论区交流:你的分页查询现在几秒?优化后又是几秒?

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有