当前位置: 首页 > 科技观察

注意避免陷阱:MySQL分页使用limit+orderby时,会出现数据重复

时间:2023-03-18 19:41:43 科技观察

0问题描述在MySQL中,我们通常使用limit进行翻页查询。例如limit(0,10)表示列出第一页10条数据,limit(10,10)表示列出第二页。但是当limit遇到orderby时,可能会出现翻到第二页的时候,第一页的记录又出现了。具体如下:SELECT`post_title`,`post_date`FROMpostWHERE`post_status`='publish'ORDERBYview_countdescLIMIT5,5当使用上面的SQL查询时,很可能是一条与LIMIT0相同的记录,5将出现。而如果用下面的方法,就不会出现重复:SELECT*FROMpostWHEREpost_status='publish'ORDERBYview_countdescLIMIT5,5但是因为post表的字段比较多,我只想用这两个字段,并没有想找出post_content。为了解决这种情况,在ORDERBY后面使用了两个排序条件来解决这个问题,如下:SELECT`post_title`,`post_date`FROMpostWHERE`post_status`='publish'ORDERBYview_countdesc,IDascLIMIT5,5从逻辑上讲,排序MySQL默认使用主键ID作为排序条件,即如果view_count相等,则默认使用主键ID作为排序条件,我们不需要添加IDasc。但事实是,当MySQL混合使用orderby和limit时,会出现排序混乱的情况。1问题分析在MySQL5.6版本中,优化器在遇到orderbylimit语句时,会进行优化,即使用优先级队列。使用优先级队列的目的是排序,当不能使用索引的顺序时使用limitn,那么在排序过程中只需要保留n条记录,虽然这不能解决所有记录的问题排序的开销是需要,但只需要排序缓冲区中的少量内存即可完成排序。MySQL5.6之所以出现第二页数据重复的问题,是因为优先级队列使用了堆排序方式,而堆排序方式是一种不稳定的排序方式,即相同的值可能被排序读出数据顺序不一致。MySQL5.5没有这个优化,所以不会出现这个问题。也就是说,MySQL5.5不存在本文提到的问题,这种情况是在5.6版本之后才出现的。看MySQL解释sql语言时的执行顺序:(1)SELECT(2)DISTINCT(3)FROM(4)JOIN(5)ON(6)WHERE(7)GROUPBY(8)HAVING(9)ORDERBY(10)LIMIT执行顺序为form...where...select...orderby...limit...,由于上面优先队列的原因,select完成后,所有的记录按照堆排序的方式排列。orderby时,只有value大的view_count往前移。但是由于限制因素,排序过程中只需要保留5条记录。view_count没有索引顺序,所以当要显示第二页数据的时候,mysql看到哪个就取哪个。因此,当排序值相同时,第一次排序是随机的,第二次执行SQL时,结果应该与第一次相同。2解决方案1.索引排序字段如果给字段加索引,可以直接按照索引的顺序进行读取和分页,这样就可以避免这个问题。2、正确理解分页分页是以排序为基础,划分编号范围。排序是数据库提供的功能,而分页是派生的应用需求。MySQL和Oracle的官方文档中都提供了limitn和rownum