当前位置: 代码网 > it编程>数据库>Mysql > MySQL 分页为什么会重复数据:补上 id 还不够(最新推荐)

MySQL 分页为什么会重复数据:补上 id 还不够(最新推荐)

2026年08月09日 Mysql 我要评论
在使用 mysql 进行分页查询时,遇到重复数据的问题通常是因为使用了不正确的查询方式。最常见的原因是使用了带有limit和offset的查询,而没有正确地使用order by来保证结果的顺序。如果没

在使用 mysql 进行分页查询时,遇到重复数据的问题通常是因为使用了不正确的查询方式。最常见的原因是使用了带有 limit 和 offset 的查询,而没有正确地使用 order by 来保证结果的顺序。如果没有正确地指定排序,数据库可能会返回重复的数据,尤其是在涉及到关联查询或多表查询时。

做列表接口时,很多人先写出这句:

select id, title, created_at
from article
order by created_at desc
limit 20 offset 20;

数据少的时候没毛病。数据一多,用户偶尔会在第二页看见第一页的记录,或者有一条怎么翻都翻不到。

这事经常被一句“深分页慢”带过去,其实是两个不同的坑:排序不稳定,以及翻页期间数据发生了变化。只优化 offset 的扫描量,修不好前一个;只在排序后补主键,也修不好后一个。

相同时间的记录,谁排前面没有保证

假设文章表里有三条数据的 created_at 都是 2026-08-08 10:00:00。sql 只要求按时间倒序,并没有规定这三条内部怎么排。

mysql 8.4 的官方手册写得很直白:如果多行的 order by 列值相同,服务端可以按任意顺序返回它们;执行计划变化时,这个顺序也可能变化。limit 本身就可能影响执行计划。

所以这不是数据库“偶尔抽风”。查询写出来的顺序本来就不完整。

先把排序补成唯一的:

select id, title, created_at
from article
order by created_at desc, id desc
limit 20 offset 20;

只要 id 唯一,任何两行都能分出先后。这里还有个小细节:两个字段必须保持同一套方向。时间倒序、同一时间下 id 也倒序,后面的游标条件才能原样对上。

这一步解决的是“同一份数据,多查几次顺序别变”。但线上列表不是一张静止的照片。

补了 id,为什么还会重复

第一页刚查完,顺序是:

105, 104, 103, 102

页面大小是 2,客户端拿到了 105, 104

这时新文章 106 插到最前面。第二页继续执行 offset 2,数据库看到的顺序已经成了:

106, 105, 104, 103, 102

跳过前两行以后,第二页返回 104, 103104 就重复了。删除或更新排序字段时,同样可能漏行。

offset 记录的是“跳过几行”,不是“上一页停在哪里”。列表头部只要发生变化,这把尺子的零点就跟着动。

接口不要求跳页时,我会改成复合游标

第一页仍然这样查:

select id, title, created_at
from article
order by created_at desc, id desc
limit 20;

返回数据时,把最后一条的 created_atid 一起交给客户端。假设最后一条是:

created_at = 2026-08-08 10:00:00
id         = 104

下一页从这个位置之后继续:

select id, title, created_at
from article
where created_at < '2026-08-08 10:00:00'
   or (created_at = '2026-08-08 10:00:00' and id < 104)
order by created_at desc, id desc
limit 20;

这里不能只传时间。时间会重复,只用 created_at < ? 会直接漏掉同一秒里排在 104 后面的记录。游标、where 条件、order by,三处都得是同一组字段。

mysql 8.0 可以配对应的降序复合索引:

create index idx_article_created_id
on article (created_at desc, id desc);

这样查询可以沿着索引从游标位置继续向后读,不必每一页都从头数到 offset。实际有没有用到,还是看 explain,别看见建了索引就默认优化器一定选它。

游标分页也不是“绝对不漏”

它解决的是最常见的滚动列表:用户往下翻时,头部不断有新数据进来。新行排在游标前面,不会把已经读过的行挤进下一页。

但如果翻页期间有人修改了旧记录的 created_at,它可能跨过游标;记录被删除后,也不可能继续返回。导出、对账这类要求严格一致的任务,应该固定快照或先固化待导出的主键集合,不能把一个在线列表接口硬当快照。

还有一个取舍。游标分页很适合“下一页”和无限滚动,不擅长直接跳到第 5000 页。产品真要求随机跳页,就得接受 offset 的成本,或者另做页码到锚点的映射。

面试里被问到 mysql 分页,我不会只答“深分页用游标”。先问排序键是否唯一,再问翻页期间有没有并发写入,最后才是扫描成本。三个问题对上的 sql 不一样。

到此这篇关于mysql 分页为什么会重复数据:补上 id 还不够的文章就介绍到这了,更多相关mysql分页重复数据内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com