缺陷的背后---LIMIT M,N 分页查找

    一、问题发现篇

        最近组内做了一次典型缺陷分享时,翻阅2018年的缺陷,找到了一个让我觉得“有料”的bug(别的同事测试发现的),先大致简单的描述下这个问题:

  •   需要实现的功能:从一个DB库同步某一段时间的数据到另一个DB库(简化后的需求)。
  •   问题描述:一次同步20w条符合记录的数据,程序同步完成后,丢数据5条。
  •   问题定位:加载数据的sql,考虑到数据量大,使用了limit M,N的方法来分页加载数据,大致如下:

                      select  *  from test where Fmodify_time >= '2018-12-12 05:00:00'and Fmodify_time <= '2018-12-12 05:01:00' limit 0,2;       

          开发哥哥定位问题产生的原因是因为20w的数据里,存在大量的Fmodify_time 值相同的记录,由于未排序,导致这类数据服务器返回时会随机选择,从而导致分页取出来的数据存在重复或者丢失的问题。然后解决的办法就是,加上order by Fmodify_time 就能满足,因为Fmodify_time 是一个索引,索引排序是有序的。后面测试小哥用相同的数据验证不丢数据,事情就告一段落了。

     

     二、问题验证和解决篇

          巧着,18年下半年花了2个月时间在学习mysql,在学习limit 分页查找的官方文档里有看到这么一段话:https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

    (1)如果在使用order by列中的多个行具有相同的值,则服务器可以按任何顺序自由返回这些行,并且可能根据整体执行计划的不同而不同。换句话说,这些行的排序顺序相对于无序列是不确定的。(2)影响执行计划的一个因素是 LIMIT,因此ORDER BY 使用和不使用查询LIMIT可能会返回不同顺序的行。如果确保使用和不使用相同的行顺序很重要,请LIMIT在ORDER BY子句中包含其他列以使记录具有确定性,
比如使用主键。

          那这么说,之前开发说通过order by 索引能使得结果返回有序就是有问题的了,带着这个疑问和官方文档的demo,结合实际的这个缺陷,做了个小验证。

          (1)建表如下:    主键:id,索引:create_time

CREATE TABLE `test` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `index_time` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1

          (2)插入数据如下:

 

        (3)模拟分页查找,不使用order by。结果如下:

         测试结果:丢数据id=1的记录,id=7的数据重复导致。看来这个问题是真的存在的。那按照开发哥哥的解决方法加上order by create_time就没问题了?

      (4)模拟分页查找,使用order by + create_time(索引),如下:

            测试结果:丢数据id=7的记录,id=3的数据重复导致。看来加了索引排序,还是有问题的,开发哥哥的“解决方法”实际是不能解决问题的。

          (5)模拟分页查找,使用order by + create_time(索引)+ id(主键),如下:

         测试结果:未丢数据。

        分页查找limit M,N 的基本原理,就是服务器查找M+N条,然后返回从M条开始的后N条,导致上面问题出现的本质原因,还是mysql服务器查询后的数据排序不稳定导致。同小节的官方文档有说明当LIMIT row_count 和ORDER BY一起使用的时候,mysql不会对整个结果集排序,而是只对row_count 行的数据进行排序然后就返回。

     If you combine LIMIT row_count with ORDER BY, MySQL stops sorting as soon as it has found the first row_count rows of the sorted result, rather than
sorting the entire result. If ordering is done by using an index, this is very fast. If a filesort must be done, all rows that match the query
without the LIMIT clause are selected, and most or all of them are sorted, before the firstrow_count are found. After the initial rows have been
found, MySQL does not sort any remainder of the result set.

        三、未解之谜篇

         大致的原理已经理清了,但是还有几个问题是没想明白的:

         问题一: 如果构造测试数据的时,把id =1的记录,时间2018-12-12 05:01:00 修改为2018-12-12 04:59:00,不加order by 都不会出现丢数据和重复数据的问题:

             这是为什么?数据的不同对结果是有影响的!!!那测试的时候数据要怎么构造呢?才能保证输入的数据都是有代表的呢?这个现象背后的原理是什么呢?

  问题二:使用索引排序 order by+索引,是不稳定的?

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 2019.01.28 问题跟进

问题二:使用索引排序 order by+索引,是不稳定的?

              这个问题有“问题“,虽然order by 和 where 条件语句都是使用了index,但是并不确定所有的分页查找的sql都是使用索引排序。比如:

        从第二个sql开始,执行计划就已经开始出现Using where; Using filesort ,跟之前第一条sql的执行计划是完全不一样的, 第二条sql之后就不能利用索引来避免排序filesort并不意味着就是文件排序,其实也有可能是内存排序,这个主要由sort_buffer_size参数与结果集大小确定。MySQL内部实现排序主要有3种方式,常规排序,优化排序和优先队列排序,主要涉及3种排序算法:快速排序、归并排序和堆排序。

        在学习其他文档了解到,在5.5版本中没有这个问题。产生这个现象的原因就是5.6针对limit M,N的语句采用了优先队列,而优先队列采用堆实现,使用的是堆排序算法,比如上述的例子order by create_time limit 4,2 limit 0,2 需要采用大小为2的大顶堆;limit 2,4需要采用大小为4的大顶堆。堆排序是非稳定的(对于相同的key值,无法保证排序后与排序前的位置一致),所以导致分页重复的现象。而解决这个问题的有效方法就是可以在排序中加上唯一值,比如主键id。

       那为什么第一条执行sql的时候Extra = Using index condition,不需要访问表,直接拿数据呢? 后面sql执行全部都是Extra = Using where; Using filesort ,估计就是跟sort_buffer_size参数有关

       学习参考文档:《数据库内核月报 - 2015 / 06》 链接:http://mysql.taobao.org/monthly/2015/06/04/

                               《MySQL排序原理》 https://blog.csdn.net/eagle89/article/details/81315981

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 优化order by 方法:

 1 加大 max_length_for_sort_data 参数的设置
  在 MySQL 中,决定使用老式排序算法还是改进版排序算法是通过参数 max_length_for_ sort_data 来决定的。当所有返回字段的最大长度小于这个参数值时,MySQL 就会选择改进后的排序算法,反之,则选择老式的算法。所以,如果有充足的内存让MySQL 存放须要返回的非排序字段,就可以加大这个参数的值来让 MySQL 选择使用改进版的排序算法。

2 去掉不必要的返回字段

  当内存不是很充裕时,不能简单地通过强行加大上面的参数来强迫 MySQL 去使用改进版的排序算法,否则可能会造成 MySQL 不得不将数据分成很多段,然后进行排序,这样可能会得不偿失。此时就须要去掉不必要的返回字段,让返回结果长度适应 max_length_for_sort_data 参数的限制。

3 增大 sort_buffer_size 参数设

  这个值如果过小的话,再加上你一次返回的条数过多,那么很可能就会分很多次进行排序,然后最后将每次的排序结果再串联起来,这样就会更慢,增大 sort_buffer_size 并不是为了让 MySQL选择改进版的排序算法,而是为了让MySQL尽量减少在排序过程中对须要排序的数据进行分段,因为分段会造成 MySQL 不得不使用临时表来进行交换排序。
但是这个值不是越大越好:
1 Sort_Buffer_Size 是一个connection级参数,在每个connection第一次需要使用这个buffer的时候,一次性分配设置的内存。
2 Sort_Buffer_Size 并不是越大越好,由于是connection级的参数,过大的设置+高并发可能会耗尽系统内存资源。
3 据说Sort_Buffer_Size 超过2M的时候,就会使用mmap() 而不是 malloc() 来进行内存分配,导致效率降低。

 

 

  

转载于:https://www.cnblogs.com/loleina/p/10319315.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

angularjs 结构的两种写法(2)

app.js里面 route.js 本项目中的路由写法,路由的意思是&#xff1a;对应的跳转页面路径&#xff0c;比如此处当路由是member.user-statisttic时&#xff0c;是会跳转到url&#xff1a;http://.../user-statisttic.html页面&#xff0c;然后此页面对应的ctrl会解析也页面的参数。…

设置Eclipse中的字符集为UTF-8

Eclipse 修改字符集 默认情况下 Eclipse 字符集为 GBK&#xff0c;但现在很多项目采用的是 UTF-8&#xff0c;这是我们就需要设置我们的 Eclipse 开发环境字符集为 UTF-8&#xff0c; 设置步骤如下&#xff1a; 在菜单栏选择 Window -> Preferences -> General -> Wor…

Python数据可视化2018:数据可视化库为什么这么多?

本文最初发布于Anaconda开发者博客&#xff0c;经原作者授权由InfoQ中文站翻译并分享。 在奥斯汀举行的SciPy 2018年特别会议上&#xff0c;大量开源Python可视化工具的代表分享了他们对Python数据可视化未来的展望。我们看到了Matplotlib、Plotly、VisPy等许多库的更新。我作为…

SpringMVC工作原理

大家好&#xff0c;我是IT修真院深圳分院第十一期学员&#xff0c;一枚正直纯洁善良的JAVA程序员。 今天给大家分享一下&#xff0c;修真院官网JAVA任务二的一个知识点&#xff1a;SpringMVC工作原理 1、背景介绍 一&#xff1a;背景介绍 JavaWeb经历的几个变化&#xff1a; 1:…

shell 数组

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1&#xff09;定义数组&#xff1a; my_array(1 2 3 4) 也可这样赋值&#xff1a;my_array[4]爱 读取&#xff1a; echo ${my_array[2]…

Spring-jdbc:JdbcTemplate使用简介

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 为了使 JDBC 更加易于使用,Spring 在 JDBCAPI 上定义了一个抽象层, 以此建立一个JDBC存取框架. 作为 SpringJDBC 框架的核心, JDBC 模板…

嘉益仕(Litns)带您读懂MES系统:选型篇

自从智能制造概念提出以来&#xff0c;制造执行系统MES在国内掀起了新一波的热潮。众多企业在技术发展、政策导向和自身需要的推动下&#xff0c;纷纷上马MES请添加链接描述项目。 由此也带动了MES软件开发企业的快速发展。一夜之间MES软件开发企业遍地开花&#xff0c;MES产品…

js - 执行上下文和作用域以及闭包

首先&#xff0c;咱们通常被"执行上下文"&#xff0c;"执行上下文环境"&#xff0c;"上下文环境"&#xff0c;"执行上下文栈"这些名词搞混。那我们一一来揭秘这些名字的含义。 这一块一直比较晦涩难懂&#xff0c;还是需要仔细去斟酌斟…

Spring之JDBCTemplate

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一、Spring对不同的持久化支持&#xff1a; Spring为各种支持的持久化技术&#xff0c;都提供了简单操作的模板和回调 ORM持久化技术模…

从蚂蚁金服实践入手,带你深入了解 Service Mesh

本文整理自蚂蚁金服高级技术专家敖小剑在 QCon 上海 2018 上的演讲。我是来自蚂蚁金服中间件团队的敖小剑&#xff0c;目前是蚂蚁金服 Service Mesh 项目的 PD。我同时也是 Servicemesher 中国技术社区的创始人&#xff0c;是 Service Mesh 技术在国内最早的布道师。我今天给大…

线程组之间的JMeter传递变量

下面&#xff0c;我们将看看如何在线程组之间共享和传递变量。在开发高级JMeter脚本时&#xff0c;很可能您将拥有多个线程组。每个线程组将执行不同的请求。一个很好的例子是我们需要使用Bearer Tokens对用户进行身份验证。一个线程组执行身份验证并保存令牌。另一个线程组需要…

python第九天(9-33)

一&#xff1a;进程 进程概念 进程就是一个程序运行在一个数据集上的一次动态执行过程进程一般由程序&#xff0c;数据集&#xff0c;进程控制块组成进程控制块&#xff1a; 进程控制块用来记录进程的外部特征&#xff0c;描述进程的执行变化过程&#xff0c;系统可以利用它来控…

Python 数据类型--Bytes类型

一、Bytes类型 在Python3以后&#xff0c;字符串和bytes类型彻底分开了。字符串是以字符为单位进行处理的&#xff0c;bytes类型是以字节为单位处理的。 bytes数据类型在所有的操作和使用甚至内置方法上和字符串数据类型基本一样&#xff0c;也是不可变的序列对象。 bytes对象只…

文件结构

C语言文件名命名的规则 1、文件标识符分为两部分&#xff0c;即文件名前缀和后缀。文件名前缀的最前面要使用范围限定符——模块名&#xff08;文件名&#xff09;缩写。 2、采用小写字母命名文件&#xff0c;避免使用一些比较通俗的文件名&#xff0c;如&#xff1a;publi…

POJ 1187 陨石的秘密 (线性DP)

题意&#xff1a; 公元11380年&#xff0c;一颗巨大的陨石坠落在南极。于是&#xff0c;灾难降临了&#xff0c;地球上出现了一系列反常的现象。当人们焦急万分的时候&#xff0c;一支中国科学家组成的南极考察队赶到了出事地点。经过一番侦察&#xff0c;科学家们发现陨石上刻…

bzoj2561 最小生成树

题意&#xff1a;给你无向图&#xff0c;给定一条边&#xff0c;求至少在原图中删去多少边才能使它同时在某个最大生成树和某个最小生成树中。 解&#xff1a; 假装我们把边排序了&#xff0c;然后把所有边权小于给定边的边都加进去了。 那么我们要删的就是s到t的一个割。 最大…

robotframework基础学习(8)

变量的使用 在 Edit 标签页中主要分&#xff1a;加载外部文件、定义内部变量、定义元数据等三个部分。 &#xff08;1&#xff09;&#xff1a;加载外部文件Add Library&#xff1a;加载测试库&#xff0c;主要是[PYTHON 目录]\Lib\site-packages 里的测试库 Add Resource&…

[蓝桥杯]ALGO-188.算法训练_P0504

Anagrams指的是具有如下特性的两个单词&#xff1a;在这两个单词当中&#xff0c;每一个英文字母&#xff08;不区分大小写&#xff09;所出现的次数都是相同的。例如&#xff0c;Unclear和Nuclear、Rimon和MinOR都是Anagrams。编写一个程序&#xff0c;输入两个单词&#xff0…

beta第二天

团队成员 郑西坤 031602542 &#xff08;队长&#xff09; 陈俊杰 031602504陈顺兴 031602505张胜男 031602540廖钰萍 031602323雷光游 031602319吴志鸿 0316206341.昨天的困难 陈顺兴&#xff1a;无 廖钰萍&#xff1a;无 吴志鸿&#xff1a;没有 雷光游&#xff1a;无 郑西坤…

C语言符号

C语言运算符的优先级 一、运算符的优先级表 C 语言的符号众多&#xff0c;由这些符号又组合成了各种各样的运算符。既然是运算符就一定有其特定的优先级&#xff0c;下表就是C 语言运算符的优先级表&#xff1a; 注&#xff1a;同一优先级的运算符&#xff0c;运算次序由结合…