缺陷的背后---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,一经查实,立即删除!

相关文章

Android应用开发—Intent组件详解

转载自&#xff1a;Android中Intent组件详解 Intent是不同组件之间相互通讯的纽带&#xff0c;封装了不同组件之间通讯的条件。 Intent本身是定义为一个类别(Class)&#xff0c;一个Intent对象表达一个目的(Goal)或期望(Expectation)&#xff0c;叙述其所期望的服务或动作、与…

angularjs 结构的两种写法(2)

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

Linux指令小记(简明实用)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、ls指令,用于列出当前目录的文件 通过添加-l参数可以使文件以详情模式列出 通过添加-a参数可以将包含隐藏文件在内的全部文件列出。…

python之glob的用法

目录 获取特定扩展名的所有文件 获取特定目录下的所有文件 递归获取所有文件 转义特殊字符 iglob glob 是 Python 中用于文件模式匹配的一个模块。它使用 Unix shell-style 的通配符来进行匹配&#xff0c;并返回所有匹配的文件路径列表。 下面是一些 glob 的基本用法&am…

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

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

换位思考的最高境界是换待遇,所以,换位思考就是空话!!!

换位思考的最高境界是换待遇&#xff0c;所以&#xff0c;换位思考就是空话&#xff01;&#xff01;&#xff01; 换位思考是件说难也难&#xff0c;说容易也容易的事情。 如果你肯把你的工资待遇财富地位跟李彦宏互换一下&#xff0c;你就可以轻松理解他到底为什么非要在百度…

一个记录最近搜索历史的LRU实现

对于很多有搜索需求的功能&#xff0c;一般需要展示下最近n次的历史搜索记录&#xff0c;主要有以下几个功能点&#xff1a; 最近搜索条目放在最前面&#xff0c;最早的搜索记录放在最后。只记录最近n条数据&#xff0c;如果超过n条搜索记录&#xff0c;删除搜索时间最久远的记…

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

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

Oulipo (KMP出现次数)

The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter e. He was a member of the Oulipo group. A quote from the book: Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis su…

从夫妻吵架中看项目管理

从夫妻吵架中看项目管理&#xff08;很有意思的文章&#xff09; 首先要说明&#xff1a;和老婆吵架无论原因如何&#xff0c;无论结果如何你都是错的&#xff0c;老婆永远是对的。但是我不是神仙&#xff0c;偶尔也要吵架。但是如何让吵架也发挥作用&#xff0c;增进夫妻感情&…

SpringMVC工作原理

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

Android应用开发—如何解决handler的警告:Handler Class Should be Static or Leaks Occur

转自android handler的警告Handler Class Should be Static or Leaks Occur 在使用Handler更新UI的时候&#xff0c;我是这样写的&#xff1a; public class SampleActivity extends Activity {private final Handler mLeakyHandler new Handler() {Overridepublic void hand…

从远程(包括ftp,http等协议)地址获取文件流信息

URL url new URL("ftp://172.18.251.155:8010/recordsImg/2019-01-28/000008_1548649813267.jpg"); MultipartFile multipartFile new MockMultipartFile(fileName,fileName,"", url.openStream());转载于:https://www.cnblogs.com/baihaojie/p/10331134…

shell 数组

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

nodejs 实现文件拷贝

通过4中不通的方式实现对一个文件的拷贝 方式一&#xff1a;readFile 异步一次性读取文件再写入 //异步形式读取文件 function copyFile(url){const extName path.extname(url)const fileName path.basename(url)const dirName path.dirname(url)fs.readFile(url, (err, dat…

国家部委对4G调研:未定给中电信联通发放牌照

一场有关4G牌照发放的论战正在发酵&#xff0c;矛盾的核心在于&#xff0c;除了中移动外&#xff0c;政府是否也会向中电信和联通发放TD-LTE(中国主导的4G标准)牌照 记者 王云辉 雍忠玮 一场围绕4G的新博弈已经白热化。 “多个国家部委正在对4G展开全面调研&#xff0c;但最终如…

Luogu4735 最大异或和

题目蓝链 Description 给你一个序列&#xff0c;你需要支持以下两个操作&#xff1a; A x: 在序列尾部添加一个整数\(x\)&#xff0c;序列的长度增加\(1\)Q l r x: 询问操作&#xff0c;你需要找到一个位置\(p \in [l, r]\)&#xff0c;使得&#xff1a;\(x \bigoplus a_p \big…

Spring-jdbc:JdbcTemplate使用简介

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

Java多线程编程:变量共享分析(Thread)

Java多线程编程&#xff1a;变量共享分析&#xff08;Thread&#xff09; Java 创建线程的两种方法 此处只简单讲下自己对java多线程变量共享的理解&#xff1a; 按照进程和多线程的原理&#xff0c;同一进程内的多个线程之间的地址空间是共享的&#xff08;除去ThreadLocal&a…