首页 > 数据库 >研发日常踩坑-Mysql分页数据重复 | 京东云技术团队

研发日常踩坑-Mysql分页数据重复 | 京东云技术团队

时间:2023-10-19 12:05:39浏览次数:35  
标签:20 分页 Mysql limit MySQL 京东 排序 order

踩坑描述:

写分页查询接口,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结合实现。

作者:京东零售 马成龙

来源:京东云开发者社区 转载请注明来源

标签:20,分页,Mysql,limit,MySQL,京东,排序,order
From: https://blog.51cto.com/u_15714439/7934098

相关文章

  • mysql 导入数据
    使用navicat工具(mysqlworkbench不好用,大文件时打不开,导入数据也慢),选中数据库,右击选中“运行SQL文件”,找到文件开始执行即可 ......
  • MySQL创建用户并授权指定数据库
    创建账号CREATEUSER'账号'@'%'IDENTIFIEDBY'密码';mysql8的话需要改一下加密方式ALTERUSER'账号'@'%'IDENTIFIEDWITHmysql_native_passwordBY'密码';创建数据库,必须要有库,不然无法绑定CREATEDATABASE数据库名;把数据库权限给用户GRANTSEL......
  • 实现Springboot中MyBatisplus使用分页“@P0”附近有语法错误selectPage, IPage和Page分
    说明:QueryWrapper<Banner> warapper=newQueryWrapper<>();Page<Banner>page=newPage<>(1,1);IPage<Banner>iPage=bannerMapper.selectPage(page,warapper);输出结果还是全部的,没有limit效果。 解决:查了一天发现网上有说,没有进行配置分页插件,加上这个分页插件配置类......
  • mysql批量更新,每条数据都不一样怎么办
    UPDATEuserSETage=CASEidWHEN1THEN3WHEN2THEN4WHEN3THEN5ENDWHEREidIN(1,2,3)php示例$display_order=array(1=>4,2=>1,3=>2,4=>3,5=>9,6=>5,......
  • MySQL
    1.视图概述:视图(View)是一种虚拟存在的表。视图并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图就是一条SELECT语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。视图相......
  • MySQL 5.7生命周期终结的技术探讨
    MySQL5.7将于2023年10月31日终结生命周期。终结意味着自2023年10月31日起,MySQL官方将不再提供对MySQL5.7版本的技术支持和更新。具体来说,这意味着以下几点。安全补丁和错误修复:MySQL官方将不再发布关于MySQL5.7版本的安全补丁和错误修复。这可能会导致在未来发现的安全漏......
  • 初识数据库Mysql
    1、数据库管理系统:管理数据的软件2、关系型数据excelMySQL、Oracle、SQLServer、SQLite、DB2,⾮关系型mapRedis、MongoDB。3、数据库的管理系统(DatabaseManagementSystem)。4、Mysql现在流⾏的免费开源的关系型数据库。5、6、数据库语句分类DDLdatadefini......
  • glibc方式安装mysql
    cd/usr/local/srcmkdirmysql   useraddmysql下载wgethttps://repo.huaweicloud.com/mysql/Downloads/MySQL-5.7/mysql-5.7.33-linux-glibc2.12-x86_64.tar.gz解压tarzxvfmysql-5.7.33-linux-glibc2.12-x86_64.tar.gz移动并改名mvmysql-5.7.33-linux-glibc2.12-x86......
  • datax从oracle到mysql数据传输
    datax进行数据迁移: 所需配置:1、服务器:windows/linux(本人使用的本地windows)         2、环境:python2.6.3  和java.11.0.20(本人使用)         3、datax(https://github.com/taosdata/DataX)以上下载完成后可以通过下面命令来查看是否下载......
  • 将自定义的日志直接写入到 mysql 数据库
    将日志直接写入到MySQL数据库中可能会对性能产生影响。如果需要高性能的日志记录解决方案,则可以考虑使用专门的日志记录器,例如Elasticsearch、Logstash和Kibana(ELK)堆栈。安装MySQL数据库,并创建一个新的数据库和表来存储日志数据。使用MySQL连接器来连接到MySQL数据库。创建一个包含......