MySQL大表分页越查越慢优化方案

前言

做网站开发的同学肯定遇到过这种情况:数据库里有一张几百万行甚至上千万行的大表,做分页查询的时候,第一页第二页查得很快,但是翻到第100页、第1000页的时候,查询就变得特别慢,甚至直接卡死。

这是为什么呢?怎么优化才能让大表分页查询不卡呢?这篇文章就详细讲讲MySQL大表分页越查越慢的原因,以及对应的优化方案。

为什么分页会越查越慢

先看一下最常见的分页查询写法:

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

这个SQL的意思是:从 orders 表里,跳过100000条数据,取后面10条。

很多同学以为这个查询很快就能出来,其实不是的。MySQL执行这个SQL的时候,会先扫描前100010条数据,然后把前面的100000条扔掉,只返回最后10条。

也就是说,你翻到第1000页的时候,MySQL要先扫描100000多条数据,然后再扔掉。翻的页数越深,MySQL要扫描和扔掉的数据就越多,所以查询就会越来越慢。

优化方案

方案1:子查询优化(延迟关联)

这是最常用的优化方案。思路就是:先用索引把需要的id查出来,然后再根据id去查完整的数据。

原来的慢查询:

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

优化后的写法:

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

这样优化的好处是:子查询里的 SELECT id FROM orders ORDER BY id LIMIT 100000, 10 只需要扫描索引就能完成,不需要回表查完整数据,速度会快很多。然后再根据查出来的10个id,去关联完整的表数据,只需要10次回表操作就行。

这种优化方式也叫"延迟关联",是大表分页最常用的优化手段。

方案2:书签分页(记住上一页最后一条id)

如果你的业务场景允许(比如只允许"上一页"和"下一页",不允许直接跳页),那用书签分页是最快的。

思路就是:记住上一页最后一条数据的id,下一页直接从这个id后面开始查。

第一页:

SELECT * FROM orders ORDER BY id LIMIT 10;

假设第一页最后一条数据的id是100。

第二页:

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

这样不管你翻到第几页,查询速度都是一样快的,因为每次都是从某个id开始往后查10条,MySQL不需要扫描前面的几十万条数据。

这种分页方式的缺点是不支持直接跳页,只能一页一页往后翻。很多信息流产品(比如朋友圈、抖音)都是用这种分页方式。

方案3:覆盖索引

如果你查询的字段刚好都在索引里,那MySQL就不需要回表查完整数据了,速度会快很多。

比如你只需要查 id, title, create_time 这几个字段,那你可以建一个联合索引:

CREATE INDEX idx_title_time ON orders(title, create_time);

然后查询的时候只查这几个字段:

SELECT id, title, create_time FROM orders ORDER BY create_time LIMIT 100000, 10;

这样MySQL直接从索引里就能拿到所有需要的数据,不需要回表,查询速度会快很多。

方案4:禁止跳页查询

如果你的业务场景允许,可以考虑禁止用户直接跳页。比如只允许用户看前100页,或者只允许"上一页"和"下一页"。

因为翻到第1000页、第10000页的用户其实很少,大部分用户都是看前几页。禁止跳页可以避免大量深分页查询拖慢数据库。

常见坑

坑1:用LIMIT offset的时候,offset越大越慢

很多同学不知道LIMIT offset的原理,以为MySQL直接就跳过了前面的offset条数据。其实不是的,MySQL是先把前面的offset条数据都查出来,然后再扔掉。所以offset越大,查询越慢。

坑2:优化了子查询,但是子查询里没有用到索引

如果你的ORDER BY字段没有建索引,那就算用了子查询优化,还是会很慢。一定要确保ORDER BY的字段有索引。

坑3:书签分页的时候,id不是连续的

书签分页的前提是你的id是连续的、递增的。如果你的业务场景里id可能会被删除,导致id不连续,那书签分页可能会漏数据。这时候需要根据实际情况调整分页策略。

坑4:用count(*)查总页数很慢

大表查总页数也很慢,比如 SELECT COUNT(*) FROM orders,几百万行的表可能要查好几秒。

这个优化方案是:

  1. 如果业务不需要精确的总页数,可以用"大约XX万条"来代替
  2. 或者单独建一张表存总条数,定期更新
  3. 或者用explain估算一下大概的行数

总结

MySQL大表分页越查越慢的问题,核心原因就是LIMIT offset会先扫描offset条数据然后扔掉,offset越大越慢。

常用的优化方案:

  1. 子查询优化(延迟关联):先查id,再关联完整数据
  2. 书签分页:记住上一页最后一条id,下一页从这个id开始查
  3. 覆盖索引:只查索引里有的字段,避免回表
  4. 禁止跳页:不允许直接翻到第1000页

根据你的业务场景选择合适的优化方案,大表分页查询慢的问题就能很好地解决了。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:36