研发日常踩坑-Mysql分页数据重复

一、踩坑描述


写分页查询接口,order by和limit混用的时候,出现了排序的混乱情况 在进行第N页查询时,出现与第一前面页码的数据一样的记录。

二、问题


在MySQL中分页查询,我们经常会用limit,如:limit(0,20)表示查询第一页的20条数据,limit(20,20)表示查询第二页的数据。业务上我们通常也会在分页的时候加上排序 order by;

但是当limit和order by一起使用的时候,有可能会出现第N页的数据,竟然和前面页码的数据有重复例如:
SELECT a,b FROM table WHERE c=1 ORDER BY d desc LIMIT 0,20
使用上述SQL查询的时候,很有可能和LIMIT 20,20查出相同的某条数据。为了解决这个问题,我们在ORDER BY后面加上了ID(唯一索引页可以)排序来进行规避,如下:
SELECT a,b FROM table WHERE c=1 ORDER BY d desc,id desc LIMIT 0,20
理论上,MySQL的排序默认情况下是以主键ID作为排序条件的,也就是说,如果在条件d相等的情况下,主键id会作为默认的排序条件,不需要我们多此一举加ID asc。但是事实就是,MySQL在order by和limit同时使用的情况下,出现了排序的混乱情况

三、分析


在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版本之后才出现了这种情况。
(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时,仅把d值大的往前移动。但由于limit的因素,排序过程中只需要保留到20条记录即可,d并不具备索引有序性,所以当第二页数据要展示时,mysql见到哪一条就拿哪一条,因此,当排序值相同的时候,第一次排序是随意排的,第二次再执行该sql的时候,其结果应该和第一次结果有可能一样。

四、解决方法


  1. 尽量使用不重复的值进行排序如果在字段添加上索引,就直接按照索引的有序性进行读取并分页(这个字段如果有重复值分页会有可能出现重复)。可以最后加上ID排序,也不会影响业务
  2. 正确理解分页分页是建立在排序的基础上,进行了数量范围分割。排序是数据库提供的功能,而分页却是衍生出来的应用需求。在MySQL和Oracle的官方文档中提供了limit n和rownum<n的方法,但却没有明确的定义分页这个概念。还有重要的一点,虽然上面的解决方法可以缓解用户的这个问题,但按照用户的理解,依然还有问题:比如,这个表插入比较频繁,用户查询的时候,在read-committed的隔离级别下,第一页和第二页仍然会有重合,这个可以使用ID来规避。所以,分页一直都有这个问题,不同场景对数据分页都没有非常高的准确性要求。
  3. 一些常见的数据库排序问题不加order by的时候的排序问题用户在使用Oracle或MySQL的时候,发现MySQL总是有序的,Oracle却很混乱,这个主要是因为Oracle是堆表,MySQL是索引聚簇表的原因。所以没有order by的时候,数据库并不保证记录返回的顺序性,并且不保证每次返回都一致的。分页问题-分页重复的问题如前面所描述的,分页是在数据库提供的排序功能的基础上,衍生出来的应用需求,数据库并不保证分页的重复问题。NULL值和空串问题不同的数据库对于NULL值和空串的理解和处理是不一样的,比如Oracle NULL和NULL值是无法比较的,既不是相等也不是不相等,是未知的。而对于空串,在插入的时候,MySQL是一个字符串长度为0的空串,而Oracle则直接进行NULL值处理。深分页问题有些时候,我们需要偏移一定量数据之后,获取某些数据,就很容易想到用limit,但是,如果偏移量很大时,就会发现SQL执行起来非常非常慢了,因为,偏移量会分页读取到buffpool中,数据量大,占用的buffpool空间就会大,而这个空间大小是配置的,一般不会很大;其次需要从一开始就扫描数据,最后要舍弃前面大量数据,只保留需要的那几条数据,而且过程还有可能需要回表操作,导致了慢sql。对于这个问题的优化,建议写一个过滤条件(比如:自增主键ID或有序的字段),再与limit结合实现。

相关推荐

  • 开发者的“贴身伴侣”
  • MySQL到TiDB:Hive Metastore横向扩展之路
  • ZGC关键技术分析
  • AlmaLinux不使用 “红帽代码”,如何保持兼容RHEL
  • 头哥对谈厉启鹏:开源技术的应用和商业价值分析
  • Mojo登陆Mac,比Python快90,000倍
  • IT BOOK多得,2023年度清华社“荐书官”有奖招募啦!
  • 一文掌握在PyCharm中正确设置Python项目
  • 一个人将模型训练单机平台升级成分布式
  • 突然暴雷!MariaDB 决定放弃两款核心产品!专家猛烈抨击!
  • Web 应用程序的“三元悖论”
  • 赞!加速AI原生应用商业化,百度智能云出新招!
  • 发现一个程序员偷偷搞私活的好地方,最少1w起!
  • 这个奇葩 App,试图降低性犯罪
  • AI炒股,一个被严重低估的方向!
  • 今年,VC开始纷纷出手找“配角”
  • 苹果「最实惠」Apple Pencil仅需79刀,USB-C但无压感!网友评价:褒贬不一
  • 斯坦福公开十大主流模型透明度!Llama 2位列第一,GPT-4透明差,LeCun炮轰:盈利完全理解
  • 估值860亿美元!OpenAI再抛员工股票,仅次SpaceX,成顶级明星独角兽
  • MIT惊人再证大语言模型是世界模型!LLM能分清真理和谎言,还能被人类洗脑