MySQL分页查询怎么写?LIMIT OFFSET用法详解

来源:Oracle教程作者:零壳头衔:程序员
导读:本期聚焦于零壳创作的《MySQL分页查询怎么写?LIMIT OFFSET用法详解》,敬请观看详情。查询结果动辄上万条,一次性全部返回既慢又不现实,分页查询因此成为MySQL使用频率最高的操作之一。LIMIT配合OFFSET可以轻松截取指定范围的数据,比如LIMIT 10 OFFSET 20表示跳过前20条再取10条。但简单语法背后藏着不少坑:OFFSET值过大时性能会急剧下降,MySQL仍需扫描并丢弃被跳过的行;ORDER BY缺少索引时还可能出现分页数据重复或丢失的问题。本文将系统讲解LIMIT和OFFSET的基本语法、执行原理,对比写法差异,并给出覆盖索引、延迟关联、书签方式等大分页优化方案,同时分析深分页场景下的性能瓶颈与解决思路,帮助你写出既正确又高效的分页SQL。

当表里的数据量达到几万甚至几百万行时,前端页面不可能把所有数据一次性展示出来,通常的做法是每页显示10条或20条,用户点击下一页时再查询下一段数据,这就是分页查询。MySQL提供了LIMIT和OFFSET关键字来完成这件事,用法看似简单,但不同的写法在性能和数据准确性上差别很大。这篇文章从基本语法讲起,逐步深入到执行原理和大分页优化。

MySQL分页查询怎么写?LIMIT OFFSET用法详解

LIMIT和OFFSET的基本语法与常见写法

MySQL中最常见的分页写法有两种。LIMIT 10 OFFSET 20表示跳过前20行,然后返回10行数据;等价的简写形式是LIMIT 20, 10,逗号前的数字是偏移量,逗号后的数字是返回的行数。两种写法效果完全一样,日常开发中简写形式更常见。

例如查询用户表第3页的数据,每页显示20条,第一页的偏移量是0,第3页的偏移量就是 (3-1) × 20 = 40,对应的SQL如下:

-- 写法一:LIMIT 行数 OFFSET 偏移量
SELECT id, username, email FROM users
ORDER BY id
LIMIT 20 OFFSET 40;

-- 写法二:LIMIT 偏移量, 行数(逗号写法)
SELECT id, username, email FROM users
ORDER BY id
LIMIT 40, 20;

有几点细节需要注意。LIMIT 0, 20表示从第一行开始取20条,偏移量是从0开始计数的,这一点和数组下标类似,初学者容易把第一页写成OFFSET 1,导致第一页永远少一条数据。另外,如果偏移量超出了总行数,比如表里只有100行却写了LIMIT 20 OFFSET 200,MySQL不会报错,只是返回空结果集。还有一种特殊情况,只写LIMIT 10表示从头取10条,等价于LIMIT 10 OFFSET 0,常用来查询前N条记录。

ORDER BY与分页:必须先解决排序问题

分页的前提是数据有稳定的顺序,否则每一页的内容就是随机的,翻页时可能出现同一条记录在不同页重复出现的问题。这就要求分页SQL必须配合ORDER BY使用,而且排序字段最好是唯一字段或者组合起来唯一的字段,比如主键id。

如果排序字段不唯一,比如按created_time排序,而很多记录的时间相同,MySQL在不同查询中的排序结果可能不稳定。第一页取到的某条数据,在查询第二页时可能因为排序顺序变化又出现了,或者某条数据被跳过去了。解决办法是加上一个唯一字段做第二排序条件:

-- 不稳定的写法:created_time 可能重复
SELECT id, username FROM users
ORDER BY created_time
LIMIT 20 OFFSET 40;

-- 稳定的写法:加上 id 保证排序唯一
SELECT id, username FROM users
ORDER BY created_time, id
LIMIT 20 OFFSET 40;

另外要注意排序字段是否有索引。如果ORDER BY的字段没有索引,MySQL会先扫描所有符合条件的行,进行文件排序(Using filesort),数据量大时CPU和内存开销都不小。用EXPLAIN分析执行计划,如果Extra列出现了Using filesort,就应该考虑为排序字段建立索引,让MySQL直接利用索引的有序性,避免额外的排序步骤。

深分页性能问题:为什么OFFSET越大越慢

LIMIT OFFSET最大的问题在于OFFSET的工作方式。执行LIMIT 10 OFFSET 1000000时,MySQL并不是直接定位到第1000001行,而是从头开始扫描,先取出1000010行,再把前1000000行丢弃,只返回最后10行。被跳过的行虽然不返回给客户端,但扫描和读取的开销一点没少,offset越大,浪费的工作越多。

在一个千万级数据的表上做测试,OFFSET为10时查询耗时几毫秒,OFFSET为500万时可能需要好几秒,性能差距上百倍。这就是很多系统翻到几百页之后越来越卡的根本原因。可以用EXPLAIN观察:即使只返回10行,rows列显示的扫描行数仍然是offset + limit的总和。

针对深分页,业内有几种成熟的优化方案。第一种是覆盖索引加延迟关联:先在索引上完成分页定位拿到主键,再回表取完整字段,大幅减少回表次数:

-- 直接分页:需要回表 offset+limit 次
SELECT id, username, email, created_time
FROM users
ORDER BY id
LIMIT 10 OFFSET 1000000;

-- 延迟关联:子查询只扫描索引,回表仅10次
SELECT u.id, u.username, u.email, u.created_time
FROM users u
INNER JOIN (
    SELECT id FROM users
    ORDER BY id
    LIMIT 10 OFFSET 1000000
) t ON u.id = t.id;

第二种方案是书签方式,也叫游标分页或keyset分页。它的思路是记住上一页最后一条记录的排序值,下一页查询时用WHERE条件直接定位,完全不使用OFFSET:

-- 记住上一页最后的 id 是 1000000,下一页直接从它之后取
SELECT id, username, email
FROM users
WHERE id > 1000000
ORDER BY id
LIMIT 10;

这种写法无论翻到第几页,扫描的行数始终接近limit值,性能稳定。它的限制是只能支持上一页下一页的连续翻页,无法直接跳转到任意页。如果产品上确实需要跳页,可以限制最大页数,或者结合延迟关联方案,把OFFSET控制在合理范围内。绝大多数互联网产品(比如信息流、订单列表)都采用游标分页,正是因为它在大数据量下表现稳定。

分页总数的计算与开发实践建议

分页页面通常还需要展示总记录数和总页数,常见的做法是执行SELECT COUNT(*) FROM users WHERE ...。在InnoDB引擎下COUNT需要扫描索引,数据量大时也比较慢。如果业务不要求精确总数,可以改为查询固定阈值,比如LIMIT 200001只取到20万就停,超过就显示20万+,很多电商和社交产品都是这么处理的。

综合来看,写分页SQL时建议遵循几个原则:分页必须配合ORDER BY且保证排序唯一;排序字段尽量走索引;小数据量直接用LIMIT OFFSET即可,不必过度优化;数据量大或翻页深时优先考虑游标分页,其次选择延迟关联;最后别忘了在上线前用EXPLAIN检查执行计划,确认扫描行数在预期范围内。掌握这些要点,就能应对MySQL分页的绝大多数场景。

MySQL分页LIMIT OFFSET分页查询优化修改时间:2026-09-04 18:54:38

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260904/50416.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。