问题
最近在项目中遇到一个特别的问题,使用 order by
排序并进行分页查询的时候,数据会出现重复
# 当xx不存在索引,且有xx相同的行是,可能出现分页数据重复问题
select * from table order by xx limit 0,10
资料
在MySQL 5.6
的版本上,优化器在遇到order by limit
语句的时候,做了一个优化,即使用了priority queue
。使用 priority queue
的目的,就是在不能使用索引有序性的时候,如果要排序,并且使用了limit n
,那么只需要在排序的过程中,保留n条记录即可,这样虽然不能解决所有记录都需要排序的开销,但是只需要 sort buffer 少量的内存就可以完成排序。之所以5.6出现了第二页数据重复的问题,是因为 priority queue
使用了堆排序的排序方法,而堆排序是一个不稳定的排序方法,也就是相同的值可能排序出来的结果和读出来的数据顺序不一致。
造成原因
基于这个我们就基本知道为什么分页会不准了,因为我们排序的字段是update_date
,正好又有几个相同的值的行,在实际执行时返回结果对应的行的顺序是不确定的。对应上面的情况,第一页返回的update_date
为2020-01-01
的数据行,可能正好排在前面,而第四页查询时update_date
为2020-01-01
的数据行正好排在后面,所以第四页又出现了。
因为数据库同一字段的值是有多条数据相同的,order by 字段名
再加上 limit
就会出现问题了
解决办法
唯一的字段加在 order by 中
找一个唯一的字段加在 order by
中
如果想在Limit存在或不存在的情况下,都保证排序结果相同,可以额外加一个排序条件。例如id
字段是唯一的,可以考虑在排序字段中额外加个id
排序去确保顺序稳定。
所以上面的情况下可以在SQL再添加个排序字段,比如id字段,这样分页的问题就解决了。修改后的SQL可以像下面这样:
#增加一个唯一的字段(唯一键)
select * from table order by update_date,id limit 0,10
降低数据库版本至5.5
5.5 没有这个优化,所以也就不会出现这个问题。