Order By Limit不稳定性

news2025/1/18 7:24:13

文章目录

    • 前置
    • 解决不确定性
    • 场景1 Order By索引
        • 1.1 背景
        • 1.2 不确定性产生原因
            • 1.2.1 正常情况下
            • 1.2.2 但是
        • 1.3 补充
        • 1.4 场景1总结
    • 场景2 Order by id
        • 2.1 背景
        • 2.2 不会产生不确定性
            • 原因1
            • 原因2
        • 2.3 推荐使用方式
    • 场景3 filesort
        • 3.1 背景
        • 3.2 不确定性产生原因
        • 3.3 内存排序和磁盘临时文件排序
        • 3.4 若上述SQL filesort使用了内存排序
            • 3.4.1 MySQL源码关于内存排序算法的选择
            • 3.4.2 问题1:什么情况下会使用优先队列
            • 3.4.3 问题2:如何快速判断是否使用了优先级队列
            • 3.4.4 问题3:如何前置判断,本次查询是否会使用优先队列
            • 3.4.5 问题4:我们写的sql是否会使用优先级队列
        • 3.5 若上述SQL filesort使用到了磁盘临时文件排序
            • 3.5.1 描述
            • 3.5.2 磁盘临时文件排序不确定性产生原因
      • 参考文档:

前置

MySQL 5.7官网给出了 order by limit一起使用,可能会产生不确定性:order by limit
不确定如图2.54
在这里插入图片描述

解决不确定性

order by limit一起使用时,order by后字段中需要有主键id或唯一键

场景1 Order By索引

1.1 背景
  • user表联合索引为city_name、主键id
select * from user where city = 'shanghai' order by name limit 0,5
  • user表联合索引city_name的叶子节点存储的数据如下
citynameid
beijingzhang三1
shanghaili四20
shanghaili四3
shanghaitian七40
shanghaiwang五5
shanghaiwang五60
shanghaizhu八7
xuzhouma朋80
1.2 不确定性产生原因
1.2.1 正常情况下
  • explain此SQL,Extra不会显示filesort即不需要排序;
  • 联合索引为city_name,所以数据在插入的时候就已经按照city_name排序好了;
  • 上述SQL取数时,直接从联合索引中拿出排序好的数据,并返回即可,所以存的时候是什么样顺序,取的时候也一定是什么样的顺序,不会产生不确定性。
1.2.2 但是

1)情况1

  • 假如两次查询期间发生了数据新增、删除、更新等操作,而且这些变更操作正好有city为shanghai的数据
  • 就可能导致联合索引city_name数据页发生了变化(重排数据顺序,甚至分页)等
  • 此时会导致查询的不确定性(漏查和重复查)
  • eg:第一次查询limit 0,5结果id为: [20,3,40,5,60]
    • 此时insert了一条数据(100,“shanghai”, “ma朋”)
    • 联合索引就可能发生了变化,id=100的这条数据,会排在id=40的数据前面(city同为shanghai,ma朋排序先于tiant)
    • 第二次查询limit0,5结果id集变为[20,3,100,40,5]

2)情况2

  • 假如user表除了city_name索引还有city_address联合索引,则查询会产生不确定性原因;
  • 两次查询可能会选择不同的联合索引,返回的数据自然不同。
1.3 补充

上述场景1和

先从db查询SQLselect *from t where city = 'shanghai' limit 0,5;
再Java程序中按照name排序返回

的这种查询方式区别:

  • 假如每次查询都force index city_name联合索引,则和场景1查询没有任何区别;
  • 本质都是使用city_name联合索引进行查询;
  • 若还有其他city_address索引,则补充SQL每次执行时可能选择city_address也可能选择city_name
1.4 场景1总结
  • 如果业务上可以保障查询期间不会有数据变更,并且每次查询都能使用相同的联合索引;

  • 则场景1不会产生数据不确定性并

  • 且下面两条SQL查询基本无区别代码块

    SELECT * from user where city = 'shanghai' order by name limit 0,5;
    SELECT * from user where city = 'shanghai'limit 0,5;
    

场景2 Order by id

2.1 背景
  • user表联合索引为:city_name、主键id、

    SELECT * from user where city ='shanghai' order by id limit 0,5;
    
2.2 不会产生不确定性
原因1

查询结果的展示顺序遵循以下描述

  • 如果查询使用到了索引,则结果按照索引字段顺序展示;

  • 如果查询没有使用到索引,则结果按照主键id顺序展示;

  • 如果查询使用到了索引,又order by id,则结果按照逐渐id顺序展示。

    所以场景2的查询,无论执行计划是什么样子的,查询结果总是按照主键id顺序展示,所以不会产生不确定性。

原因2

即使有数据新增、删除、更新,也不会产生不确定性

  • 第一次查询limit 0,5,结果id为:[20,3,40,5,60];
  • 此时insert了一条数据(100,“shanghai”,“ma朋”),联合索引就可能发生了变化,id=100的这条数据,会排在id=40的数据前面(city同为shanghai,ma朋排序先于tian七)
  • 第二次查询limit 0,5结果id集仍为[20,3,40,5,60]
  • 即使数据新增(100,‘shanghai’, ‘ma朋’),即使联合索引city_name需要调整数据顺序,但order by id,数据仍是按照主键id排序然后返回
2.3 推荐使用方式

相较于上述SQL,更推荐使用下述性能更好的书写方式

SELECT * from user where city = 'shanghai' order by name, id limit 0,5;

原因:场景2的书写方式,MySQL在执行时,可能直接按照主键id进行全表扫描,explain显示type = index、Extra = using where

场景3 filesort

3.1 背景
  • user表联合索引为:city_name、主键id

    SELECT * from user where city = 'shanghai' order by address limit 0,5;
    
3.2 不确定性产生原因
  • 上述SQL在执行时,会产生排序,即explain结果Extra中显示filesort;
  • filesort可能为内存排序,也可能是磁盘临时文件排序;
  • filesort时,如果查询出来的数据按照order by的字段(比如address),有相同的数据,且排序算法是不稳定性算法,则会产生不确定性。

详见MySQL5.7官方文档

  • limit、order byOrder by limit一同使用时产生不确定性
3.3 内存排序和磁盘临时文件排序
filesort内存排序磁盘临时文件排序
触发条件参与排序的数据大小<= sort_buffer_size参与排序的数据大小>sort_buffer_size
使用到的排序算法插入排序算法、快速排序、堆排序多路归并排序算法
3.4 若上述SQL filesort使用了内存排序
内存排序算法使用场景是否为稳定性排序算法是否会产生不确定性
插入排序处理小数据集或近乎有序的数据集时,优先选择
堆排序如果记录数量非常大,MySQL会优先选择堆排序
快速排序或快排序

如果排序算法不是稳定性算法 并且 参与内存排序的数据按照order by字段有相同的数据时,则可能产生返回数据的不确定性

3.4.1 MySQL源码关于内存排序算法的选择

filesort.cc文件关于快速排序和堆排序(优先队列)的分析

3.4.2 问题1:什么情况下会使用优先队列
  • 当order by的字段重复率特别高的时候,且带着limit查询,MySQL在满足某些条件下就会使用优先队列(PQ,priority queue)
  • limit N -> 本质就是找TOP N->优先级队列->堆排序
  • 优先级队列是一种优化技术,用于提高ORDER BY和LIMIT组合查询的性能,其查询结果是不确定性的
3.4.3 问题2:如何快速判断是否使用了优先级队列
  • 查看执行计划结果(可以使用Sequel Pro客户端执行SQL)
SET optimizer_trace='enabled=on';
SELECT * FROM ratings ORDER BY category LIMIT 300,31;
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`;
SET optimizer_trace='enabled=off';
  • 分析执行计划结果
"filesort_priority_queue_optimization":{
	"limit": 331, //limit m,n(m+n)
	"rows_estimate":992// 即MySQL源码中max_rows < num_rows / PO_slowness中的num_rows,MySQL优化器对需要进行排序的行的数量所做的估计。优化器对需要进行排序的行的数量所做的估计,动态变化值影响因素有:可用内存、系统负载、查询复杂性、表的统计信息、	
	"row_size":13,//一行数据的大小	
	"memory_available": 8388608,//sort_buffer_szie =8m(默认1M)	
	"chosen": false,//没有选择优先队列,为true则表示选择了PQ优先队列,没有cause原因	
	"cause":"quicksort_is_cheaper"//原因:快排性价比更高
}

"filesort_summary":{	
	"rows": 60,// 排序过程中会持有的行数	
	"examined_rows": 60,//sql语句where条件count(*)	
	"number_of_tmp_files": 0,//磁盘排序,使用的临时文件个数=0表示内存排序	
	"sort buffer size":20832,//使用内存排序时,使用到的内存大小
}

网上类似执行计划结果图如图2.55右图:使用了优先队列
在这里插入图片描述

chosen:true表示选择了优先级队列

3.4.4 问题3:如何前置判断,本次查询是否会使用优先队列

1)参考MySQL源码:MySQL Sever源码判断什么时候使用优先队列,简介:

  • s首先确定排序逻辑所处代码文件:filesort.cc
  • 然后在文件filesort.cc中搜索关键字:priority_queue
  • 找到check_if_pq_applicable方法,此方法的返回结果表明排序过程中是否使用到了优先队列(堆排序),同时此方法内容也有判断什么时候使用堆排序,什么时候使用快排

注意:

为了便于理解,后续filesort.cc源码中字段的值,我直接一一对应为执行计划结果filesort_priority_queue_optimization中的字段值

2)源码分析

// memory_available: 本质是sort_buff_size,对应执行计划中memory_available字段,默认1M
// max_record_length():即单行数据的大小,对应执行计划中的row_size
// sizeof(char*):指针的大小=8字节
const ulong num_available_keys = memory_available / (param->max_record_length() + sizeof(char *));

param->max_rows_per_buffer = (uint)param->max_rows +1;

// 情况一:整个rows_estimate是否都可以放入内存中,即内存够用
// num_rows:对应执行计划中rows_estimate	
if (num_rows < num_available_keys) {	
// max_rows 就是limit中M+N,对应执行计划中limit值	
// num_rows:对应执行计划中rows_estimate	
// PQ_slowness = 3	
	if (param->max_rows < num_rows / PQ_slowness) {	
		filesort_info->set_max_size(memory_available, param->max_record_length());
		trace_filesort.add("chosen",true);//选择PQ
		return filesort_info->max_size_in_bytes()>0;//true表示排序操作将在内存中执行;false则可能表示排序操作将使用磁盘上的临时文件来辅助排序
	} else {	
		// PO will be slower.不是优先级队列,直接使用快排	
		// 会一批一批的给sort buffer进行内存快速排序,结果放入排序临时文件,最终使对所有排好序的临时文件进行归并排序,得到最终的结果	
		trace_filesort.add("chosen", false).add_alnum("cause","sort_is_cheaper"); 
        return false;
}
    
//情况二:	
// Do we have space for LIMIT rows in memory?
// 即使整个源数据集不适合内存排序,如果至少有足够的空间来存储查询限制的行数(M+N),代码将仍然选择使用PQ
if (param->max_rows_per_buffer < num_available_keys) {
	filesort_info->set_max_size(memory_available,param->max_record_length());	
	trace_filesort.add("chosen",true);
	return filesort_info->max_size_in_bytes()>0;	
}

文字解释上述源码如下:

情况一:
1、内存够(我司默认sort_buffer_size=8M)
	即num_rows < num_available_keys
	即num_rows < sort_buffer_size /每行数据的size(执行计划结果中的filesort_priority_queue_optimization下的row_size)
    含义:内存8M、每行数据13bytes,指针大小8字节,总共可以放入x条数据,假如num_rows<x,则说明内存够!
    代入执行计划中的数值:992<8388608/ (13+8)

2、max_rows < num_rows / PQ_slowness
	当满足1时,MySQL会对比优先级队列排序和快排序的开销,选择一个比较合适的排序算法
	快速排序算法效率高于堆排序,但是堆排序实现的优先级队列,无需排序完所有的元素,就可以得到order by limit的结果
    MySQL官方认为快速排序的速度是堆排序的3倍
	所以,在满足1、的情况下&&满足公式max_rows < num_rows / PQ_slowness时会使用优先队列即使用堆排
	堆排序时,MySQL会把sort buffer作为priority queue
    如果不满足公式则仍用快排序。
	代入执行计划中的数值:331<992/3,显然不满足,则chosen:false
满足1、且满足2、时,则情况一在内存中使用buffer_sort作为优先级队列进行堆排序。只满足1、则使用内存快排

情况二:
	如果不满足场情况一中的1、则判断:Limit M+N < num_available_keys
	代入数值:331<8388608/ (13+8)
	如果满足,也可以使用buffer_sort作为PQ,在内存中进行堆排序
3.4.5 问题4:我们写的sql是否会使用优先级队列
select * from t where x order by xxx limit 200,200;	
SET optimizer_trace='enabled=on';
select * from t where x order by xxx limit 200,200;
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`;
SET optimizer_trace='enabled=off';

执行结果分析
"filesort_priority_queue_optimization":{
	"limit": 400,//limit m,n(m+n)
	"rows_estimate": 992, // 即max_rows < num_rows / P0_slowness中的num_rows (Estimate of 							number of rows in source record set即原表中预估计满足查询条件的行数)
	"row_size": 13,//一行数据的大小	
	"memory_available":8388608,//sort_buffer_szie =8m"chosen": false,//没有选择优先队列
	"cause": "quicksort_is_cheaper"//原因	
}

"filesort_summary":{
	"rows": 60,// 排序过程中会持有的行数	
	"examined_rows": 60,//参与排序的行数	
	"number_of_tmp_files": 0,//磁盘排序,使用的临时文件个数=0表示内存排序	
	"sort_buffer_size":20832,//使用内存排序时,使用到的内存大小	
	"sort_mode": "<sort_key,rowid>"//排序方式(rowid或者全字段排序)	
}
.

根据执行计划结果,先判断3.5.3中情况一、再判断是否满足情况二

3.5 若上述SQL filesort使用到了磁盘临时文件排序
3.5.1 描述
  • 当数据无法全部加载到内存排序时,会使用磁盘临时文件排序;
  • 这个过程会将涉及到数据分割成多个“块”,如果每个“块”也需要进行排序,则可能会使用快速排序或堆排序来分割和重组数据;
  • 然后在外部磁盘临时文件排序阶段使用归并排序,将多个已排序好的“块”合并成一个有序的块。
  • 综上:“多路归并排序”(muli-way merge sort)的技术,是一种结合了快速排序维排序和归并排序特点的方法,即在内存排序阶段,它可能会使用快速排序或堆排序来分割和重组数据,然后在外部排序阶段使用归并排序来合并来自不同临时文件的数据
3.5.2 磁盘临时文件排序不确定性产生原因

即使最终合并多个“块”时使用到了稳定的排序算法归并排序,但每个“块”在排序阶段,可能使用到快排和堆排序,鉴于二者都是不稳定性排序算法,故不稳定原因同上3.5

参考文档:

什么情况下order by limit会使用优先队列

filesort_priority_queue_optimization

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

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

相关文章

【监控】grafana图表使用快速上手

目录 1.前言 2.连接 3.图表 4.job和path 5.总结 1.前言 上一篇文章中&#xff0c;我们使用spring actuatorPrometheusgrafana实现了对一个spring boot应用的可视化监控。 【监控】Spring BootPrometheusGrafana实现可视化监控-CSDN博客 其中对grafana只是打开了一下&am…

【前端素材】推荐优质后台管理系统Welly平台模板(附源码)

一、需求分析 后台管理系统&#xff08;或称作管理后台、管理系统、后台管理平台&#xff09;是一种专门用于管理网站、应用程序或系统后台运营的软件系统。它通常由一系列功能模块组成&#xff0c;为管理员提供了管理、监控和控制网站或应用程序的各个方面的工具和界面。以下…

Vue3 学习笔记(Day4)

「写在前面」 本文为尚硅谷禹神 Vue3 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. Vue3 学习笔记&#xff08;Day1&#xff09; 2. Vue3 学习笔记&#xff08;Day2&…

探索Java11新世界:JDK 11新特性详解

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

高频面试题整理(一)

文章目录 平台无关性如何实现&#xff1f;JVM如何加载 .class文件&#xff1f;什么是反射?谈谈ClassLoader谈谈类的双亲委派机制类的加载方式Java的内存模型?JVM内存模型-jdk8程序计数器&#xff1a;Java虚拟机栈局部变量表和操作数栈&#xff1a; Java内存模型中堆和栈的区别…

153.寻找旋转排序数组中的最小值(二分查找)

一、题目 . - 力扣&#xff08;LeetCode&#xff09; 153. 寻找旋转排序数组中的最小值 二、代码 class Solution { public:int findMin(vector<int>& nums) {int left 0;int right nums.size()-1;int mid (leftright)/2;while(left<right){if(nums[mid]>…

ROS 2的前世今生 | ROS 2学习笔记

自2015年底首次踏入ROS&#xff08;Robot Operating System&#xff09;的世界以来&#xff0c;我在机器人领域的旅程已近九年。这段历程始于团队几位志同道合的朋友在业余时间的自发学习&#xff0c;逐渐演变成成立了一个致力于英特尔硬件平台与ROS框架集成优化的专业团队&…

Curator基本使用

文章目录 1. 基本操作1.1 建立连接1.2 创建结点1.3 查询结点查询数据查询子结点查看结点信息 1.4 修改结点普通修改带乐观锁的修改 1.5 删除删除单个结点删除带子结点的结点必须成功的删除带回调函数的删除 2. 监听器事件2.1 NodeCache单一结点连续监听2.2 PathChildrenCache监…

2024.2.25 在centos8.0安装docker

2024.2.25 在centos8.0安装docker 安装过程比较简单&#xff0c;按顺序安装即可&#xff0c;简要步骤&#xff1a; 一、更新已安装的软件包&#xff1a; sudo yum update二、安装所需的软件包&#xff0c;允许 yum 通过 HTTPS 使用存储库&#xff1a; sudo yum install -y …

VIO第2讲:IMU标定实验

VIO第2讲&#xff1a;IMU标定实验 文章目录 VIO第2讲&#xff1a;IMU标定实验5 IMU标定实验5.1 仿真数据产生5.1.1 c代码分析5.1.2 生成ros包数据 5.2 Allan方差实验&#xff08;港科大imu_utils&#xff09;5.2.1 安装5.2.2 运行 5.3 Allan方差实验&#xff08;matlab代码kali…

内网设备如何在互联网上能访问

应用场景 设备安装到了客户现场&#xff0c;如果要调试设备&#xff0c;当前的处理方式是技术人员出差到客户现场、让客户开通VPN、让客户安装远程工具&#xff0c;远程到客户计算机上进行调试等方法。人不在家里想远程家里的电脑&#xff0c;当前处理方式就是在家里电脑上安装…

搜索专项---IDA*

文章目录 排书回转游戏 一、排书OJ链接 本题思路:先考虑每一步的决策数量&#xff1a;当抽取长度为 i 的一段时&#xff0c;有 n−i1 种抽法&#xff0c;对于每种抽法&#xff0c;有 n−i 种放法。另外&#xff0c;将某一段向前移动&#xff0c;等价于将跳过的那段向后移动&am…

【DataTable.js】02.DataTable参考

一、Option 1.1 Features 属性类型默认值描述autoWidthbooleantrue是否自动调节单元格宽度&#xff0c;若传入了columns.width&#xff0c;可禁用该选项orderingbooleantrue是否支持排序pagingbooleantrue是否支持分页scrollXbooleanfalse是否支持横向滚动条scrollYstring启用…

USB Micro引脚及相应原理图绘制

前言&#xff1a;博主为实现绘制USB Micro输入口原理图&#xff0c;首先在 GD32F103XX的数据手册中找到引脚的功能描述&#xff0c;找到USBDM与USBDP功能&#xff0c;分别为引脚PA11与引脚PA12。然后进行相应的原理图绘制。 * USBDM。USBDM 引脚是与通用串行总线 (Universal Se…

计算机操作系统-笔记

现代操作系统阅读笔记 第一章 引论 1. 操作系统定义 操作系统是运行在内核态的软件&#xff0c;它执行两个基本上独立的任务。 隐藏计算机底层硬件的实现&#xff0c;为用户及应用程序提供一个资源集的清晰抽象。 管理计算机硬件资源。 任何操作系统的核心是它可处理的系…

Java基础常见八股文学习总结1

Java基础常见八股文学习总结1 SPI SPI 即 Service Provider Interface &#xff0c;字面意思就是&#xff1a;“服务提供者的接口”&#xff0c;我的理解是&#xff1a;专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。SPI 将服务接口和具体的服务实现分离开来…

vue项目的前端工程化思路webpack(持续更新中)

写在前面&#xff1a;现在的前端网页功能丰富&#xff0c;特别是SPA&#xff08;single page web application 单页应用&#xff09;技术流行后&#xff0c;JavaScript的复杂度增加和需要一大堆依赖包&#xff0c;还需要解决Scss&#xff0c;Less……新增样式的扩展写法的编译工…

C++ //练习 8.7 修改上一节的书店程序,将结果保存到一个文件中。将输出文件名作为第二个参数传递给main函数。

C Primer&#xff08;第5版&#xff09; 练习 8.7 练习 8.7 修改上一节的书店程序&#xff0c;将结果保存到一个文件中。将输出文件名作为第二个参数传递给main函数。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /********…

5分钟轻松帮你EasyRecovery恢复女朋友照片

相信有不少男性电脑玩家都会将女朋友的照片存放在电脑硬盘之内&#xff0c;作为珍贵的收藏和回忆。但是在某些时候&#xff0c;如果我们错误地删除了这些照片&#xff0c;或者由于系统问题导致其中的照片丢失&#xff0c;那么我们怎么找回女朋友的照片&#xff1f;这个问题就足…

【技术分享】使用nginx完成动静分离➕集成SpringSession➕集成sentinel➕集成seata

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于技术点的相关分享吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 一、 使用nginx完成动静分离 1.下载…