专栏名称: 石杉的架构笔记
专注原创、用心雕琢!十余年BAT一线大厂架构经验倾囊相授
目录
相关文章推荐
VC/PE/MA金融圈  ·  马斯克投的飞行汽车试飞成功!垂直起降,无外露 ... ·  4 小时前  
网信黑龙江  ·  V眼看龙江 | 说说我的家乡佳木斯 ·  2 天前  
网信黑龙江  ·  V眼看龙江 | 说说我的家乡佳木斯 ·  2 天前  
哈尔滨日报  ·  许勤看望慰问负责亚冬会安全保障工作人员 ·  3 天前  
新闻夜航  ·  不低于50万奖励!知名集团出手了 ·  3 天前  
51好读  ›  专栏  ›  石杉的架构笔记

如何解决MySQL order by limit语句的分页数据重复问题?

石杉的架构笔记  · 公众号  ·  · 2021-03-24 09:46

正文

点击上方蓝色“石杉的架构笔记”,选择“设为星标”

回复“PDF”获取独家整理的学习资料!


长按扫描上方二维码了解

文章来源: https://www.jianshu.com/p/544c319fd838


0 问题描述

在MySQL中我们通常会采用limit来进行翻页查询,比如limit(0,10)表示列出第一页的10条数据,limit(10,10)表示列出第二页。


但是,当limit遇到order by的时候,可能会出现翻到第二页的时候,竟然又出现了第一页的记录。


具体如下:


SELECT `post_title`,`post_date` FROM post WHERE `post_status`='publish' ORDER BY view_count desc LIMIT 5,5


使用上述SQL查询的时候,很有可能出现和LIMIT 0,5相同的某条记录。而如果使用如下方式,则不会出现重复的情况:


SELECT * FROM post WHERE post_status='publish' ORDER BY view_count desc LIMIT 5,5


但是,由于post表的字段很多,仅仅希望用这两个字段,不想把post_content也查出来。


为了解决这个情况,在ORDER BY后面使用了两个排序条件来解决这个问题,如下:


SELECT `post_title`,`post_date` FROM post WHERE `post_status`='publish' ORDER BY view_count desc,ID asc LIMIT 5,5


按理来说,MySQL的排序默认情况下是以主键ID作为排序条件的


也就是说,如果在view_count相等的情况下,主键ID作为默认的排序条件,不需要我们多此一举加ID asc。


但是事实就是,MySQL再order by和limit混用的时候,出现了排序的混乱情况。


1 分析问题

在MySQL 5.6的版本上, 优化器在遇到order by limit语句的时候,做了一个优化,即使用了priority queue


使用 priority queue 的目的,就是在不能使用索引有序性的时候,如果要排序,并且使用了limit n,那么只需要在排序的过程中,保留n条记录即可


这样虽然不能解决所有记录都需要排序的开销,但是只需要 sort buffer 少量的内存就可以完成排序。


之所以MySQL 5.6出现了第二页数据重复的问题, 是因为 priority queue 使用了堆排序的排序方法,而堆排序是一个不稳定的排序方法


也就是相同的值可能排序出来的结果和读出来的数据顺序不一致。


MySQL 5.5 没有这个优化,所以也就不会出现这个问题。


也就是说,MySQL 5.5是不存在本文提到的问题的,5.6版本之后才出现了这种情况。


再看下MySQL解释sql语言时的执行顺序:

(1)     SELECT
(2)     DISTINCT <select_list>
(3)     FROM <left_table>
(4)     <join_type> JOIN <right_table>
(5)     ON <join_condition>
(6)     WHERE <where_condition>
(7)     GROUP BY <group_by_list>
(8)     HAVING <having_condition>
(9)     ORDER BY <order_by_condition>
(10)    LIMIT <limit_number>

执行顺序依次为 form… where… select… order by… limit…



由于上述priority queue的原因,在完成select之后, 所有记录是以堆排序的方法排列的


在进行order by时,仅把view_count值大的往前移动。但由于limit的因素,排序过程中只需要保留到5条记录即可


view_count并不具备索引有序性, 所以当第二页数据要展示时,mysql见到哪一条就拿哪一条


因此,当排序值相同的时候,第一次排序是随意排的,第二次再执行该sql的时候,其结果应该和第一次结果一样。


2 解决方法


(1)索引排序字段


如果在字段添加上索引,就直接按照索引的有序性进行读取并分页,从而可以规避遇到的这个问题。







请到「今天看啥」查看全文