order by是什么意思?order by详解
前言
(相关资料图)
日常开发中,我们经常会使用到order by,亲爱的小伙伴,你是否知道order by 的工作原理呢?order by的优化思路是怎样的呢?使用order by有哪些注意的问题呢?本文将跟大家一起来学习,攻克order by~
一个使用order by 的简单例子
假设用一张员工表,表结构如下:
CREATE TABLE `staff` (`id` BIGINT ( 11 ) AUTO_INCREMENT COMMENT "主键id",`id_card` VARCHAR ( 20 ) NOT NULL COMMENT "身份证号码",`name` VARCHAR ( 64 ) NOT NULL COMMENT "姓名",`age` INT ( 4 ) NOT NULL COMMENT "年龄",`city` VARCHAR ( 64 ) NOT NULL COMMENT "城市",PRIMARY KEY ( `id`),INDEX idx_city ( `city` )) ENGINE = INNODB COMMENT "员工表";
表数据如下:
我们现在有这么一个需求:查询前10个,来自深圳员工的姓名、年龄、城市,并且按照年龄小到大排序。对应的 SQL 语句就可以这么写:
select name,age,city from staff where city = "深圳" order by age limit 10;
这条语句的逻辑很清楚,但是它的底层执行流程是怎样的呢?
order by 工作原理
explain 执行计划
我们先用Explain关键字查看一下执行计划
执行计划的key这个字段,表示使用到索引idx_city
Extra 这个字段的 Using index condition表示索引条件
Extra 这个字段的 Using filesort表示用到排序
我们可以发现,这条SQL使用到了索引,并且也用到排序。那么它是怎么排序的呢?
全字段排序
MySQL 会给每个查询线程分配一块小内存,用于排序的,称为 sort_buffer。什么时候把字段放进去排序呢,其实是通过idx_city索引找到对应的数据,才把数据放进去啦。
我们回顾下索引是怎么找到匹配的数据的,现在先把索引树画出来吧,idx_city索引树如下:
idx_city索引树,叶子节点存储的是主键id。还有一棵id主键聚族索引树,我们再画出聚族索引树图吧:
我们的查询语句是怎么找到匹配数据的呢?先通过idx_city索引树,找到对应的主键id,然后再通过拿到的主键id,搜索id主键索引树,找到对应的行数据。
加上order by之后,整体的执行流程就是:
MySQL 为对应的线程初始化sort_buffer,放入需要查询的name、age、city字段;
从索引树idx_city, 找到第一个满足 city="深圳’条件的主键 id,也就是图中的id=9;
到主键 id 索引树拿到id=9的这一行数据, 取name、age、city三个字段的值,存到sort_buffer;
从索引树idx_city拿到下一个记录的主键 id,即图中的id=13;
重复步骤 3、4 直到city的值不等于深圳为止;
前面5步已经查找到了所有city为深圳的数据,在 sort_buffer中,将所有数据根据age进行排序;
按照排序结果取前10行返回给客户端。
执行示意图如下:
将查询所需的字段全部读取到sort_buffer中,就是全字段排序。这里面,有些小伙伴可能会有个疑问,把查询的所有字段都放到sort_buffer,而sort_buffer是一块内存来的,如果数据量太大,sort_buffer放不下怎么办呢?
磁盘临时文件辅助排序
实际上,sort_buffer的大小是由一个参数控制的:sort_buffer_size。如果要排序的数据小于sort_buffer_size,排序在sort_buffer内存中完成,如果要排序的数据大于sort_buffer_size,则借助磁盘文件来进行排序
如何确定是否使用了磁盘文件来进行排序呢?可以使用以下这几个命令
## 打开optimizer_trace,开启统计set optimizer_trace = "enabled=on";## 执行SQL语句select name,age,city from staff where city = "深圳" order by age limit 10;## 查询输出的统计信息select * from information_schema.optimizer_trace
可以从 number_of_tmp_files中看出,是否使用了临时文件。
number_of_tmp_files表示使用来排序的磁盘临时文件数。如果number_of_tmp_files>0,则表示使用了磁盘文件来进行排序。
使用了磁盘临时文件,整个排序过程又是怎样的呢?
从主键Id索引树,拿到需要的数据,并放到sort_buffer内存块中。当sort_buffer快要满时,就对sort_buffer中的数据排序,排完后,把数据临时放到磁盘一个小文件中。
继续回到主键 id 索引树取数据,继续放到sort_buffer内存中,排序后,也把这些数据写入到磁盘临时小文件中。
继续循环,直到取出所有满足条件的数据。最后把磁盘的临时排好序的小文件,合并成一个有序的大文件。
TPS:借助磁盘临时小文件排序,实际上使用的是归并排序算法。
小伙伴们可能会有个疑问,既然sort_buffer放不下,就需要用到临时磁盘文件,这会影响排序效率。那为什么还要把排序不相关的字段(name,city)放到sort_buffer中呢?只放排序相关的age字段,它不香吗?可以了解下rowid 排序。
rowid 排序
rowid 排序就是,只把查询SQL需要用于排序的字段和主键id,放到sort_buffer中。那怎么确定走的是全字段排序还是rowid 排序排序呢?
实际上有个参数控制的。这个参数就是max_length_for_sort_data,它表示MySQL用于排序行数据的长度的一个参数,如果单行的长度超过这个值,MySQL 就认为单行太大,就换rowid 排序。我们可以通过命令看下这个参数取值。
show variables like "max_length_for_sort_data";
max_length_for_sort_data默认值是1024。因为本文示例中name,age,city长度=64+4+64 =132 < 1024, 所以走的是全字段排序。我们来改下这个参数,改小一点,
## 修改排序数据最大单行长度为32set max_length_for_sort_data = 32;## 执行查询SQLselect name,age,city from staff where city = "深圳" order by age limit 10;
使用rowid 排序的话,整个SQL执行流程又是怎样的呢?
MySQL 为对应的线程初始化sort_buffer,放入需要排序的age字段,以及主键id;
从索引树idx_city, 找到第一个满足 city="深圳’条件的主键 id,也就是图中的id=9;
到主键 id 索引树拿到id=9的这一行数据, 取age和主键id的值,存到sort_buffer;
从索引树idx_city拿到下一个记录的主键 id,即图中的id=13;
重复步骤 3、4 直到city的值不等于深圳为止;
前面5步已经查找到了所有city为深圳的数据,在 sort_buffer中,将所有数据根据age进行排序;
遍历排序结果,取前10行,并按照 id 的值回到原表中,取出city、name 和 age 三个字段返回给客户端。
执行示意图如下:
对比一下全字段排序的流程,rowid 排序多了一次回表。
我们通过optimizer_trace,可以看到是否使用了rowid排序的:
## 打开optimizer_trace,开启统计set optimizer_trace = "enabled=on";## 执行SQL语句select name,age,city from staff where city = "深圳" order by age limit 10;## 查询输出的统计信息select * from information_schema.optimizer_trace
全字段排序与rowid排序对比
全字段排序:sort_buffer内存不够的话,就需要用到磁盘临时文件,造成磁盘访问。
rowid排序:sort_buffer可以放更多数据,但是需要再回到原表去取数据,比全字段排序多一次回表。
一般情况下,对于InnoDB存储引擎,会优先使用全字段排序。可以发现 max_length_for_sort_data参数设置为1024,这个数比较大的。一般情况下,排序字段不会超过这个值,也就是都会走全字段排序。
order by的一些优化思路
我们如何优化order by语句呢?
因为数据是无序的,所以就需要排序。如果数据本身是有序的,那就不用排了。而索引数据本身是有序的,我们通过建立联合索引,优化order by 语句。
我们还可以通过调整max_length_for_sort_data等参数优化;
联合索引优化
再回顾下示例SQL的查询计划
explain select name,age,city from staff where city = "深圳" order by age limit 10;
我们给查询条件city和排序字段age,加个联合索引idx_city_age。再去查看执行计划
alter table staff add index idx_city_age(city,age);explain select name,age,city from staff where city = "深圳" order by age limit 10;
可以发现,加上idx_city_age联合索引,就不需要Using filesort排序了。为什么呢?因为索引本身是有序的,我们可以看下idx_city_age联合索引示意图,如下:
整个SQL执行流程变成酱紫:
从索引idx_city_age找到满足city="深圳’的主键 id
到主键 id索引取出整行,拿到 name、city、age 三个字段的值,作为结果集的一部分直接返回
从索引idx_city_age取下一个记录主键id
重复步骤 2、3,直到查到第10条记录,或者是不满足city="深圳’条件时循环结束。
流程示意图如下:
从示意图看来,还是有一次回表操作。针对本次示例,有没有更高效的方案呢?有的,可以使用覆盖索引:
我们给city,name,age 组成一个联合索引,即可用到了覆盖索引,这时候SQL执行时,连回表操作都可以省去啦。
调整参数优化
我们还可以通过调整参数,去优化order by的执行。比如可以调整sort_buffer_size的值。因为sort_buffer值太小,数据量大的话,会借助磁盘临时文件排序。如果MySQL服务器配置高的话,可以使用稍微调整大点。
我们还可以调整max_length_for_sort_data的值,这个值太小的话,order by会走rowid排序,会回表,降低查询性能。所以max_length_for_sort_data可以适当大一点。
当然,很多时候,这些MySQL参数值,我们直接采用默认值就可以了。
使用order by 的一些注意点
没有where条件,order by字段需要加索引吗
日常开发过程中,我们可能会遇到没有where条件的order by,那么,这时候order by后面的字段是否需要加索引呢。如有这么一个SQL,create_time是否需要加索引:
select * from A order by create_time;
无条件查询的话,即使create_time上有索引,也不会使用到。因为MySQL优化器认为走普通二级索引,再去回表成本比全表扫描排序更高。所以选择走全表扫描,然后根据全字段排序或者rowid排序来进行。
如果查询SQL修改一下:
select * from A order by create_time limit m;
无条件查询,如果m值较小,是可以走索引的.因为MySQL优化器认为,根据索引有序性去回表查数据,然后得到m条数据,就可以终止循环,那么成本比全表扫描小,则选择走二级索引。
分页limit过大时,会导致大量排序怎么办?
假设SQL如下:
select * from A order by a limit 100000,10
可以记录上一页最后的id,下一页查询时,查询条件带上id,如:where id > 上一页最后id limit 10。
也可以在业务允许的情况下,限制页数。
索引存储顺序与order by不一致,如何优化?
假设有联合索引 idx_age_name, 我们需求修改为这样:查询前10个员工的姓名、年龄,并且按照年龄小到大排序,如果年龄相同,则按姓名降序排。对应的 SQL 语句就可以这么写:
select name,age from staff order by age ,name desc limit 10;
我们看下执行计划,发现使用到Using filesort。
这是因为,idx_age_name索引树中,age从小到大排序,如果age相同,再按name从小到大排序。而order by 中,是按age从小到大排序,如果age相同,再按name从大到小排序。也就是说,索引存储顺序与order by不一致。
我们怎么优化呢?如果MySQL是8.0版本,支持Descending Indexes,可以这样修改索引:
CREATE TABLE `staff` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT "主键id", `id_card` varchar(20) NOT NULL COMMENT "身份证号码", `name` varchar(64) NOT NULL COMMENT "姓名", `age` int(4) NOT NULL COMMENT "年龄", `city` varchar(64) NOT NULL COMMENT "城市", PRIMARY KEY (`id`), KEY `idx_age_name` (`age`,`name` desc) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT="员工表";
使用了in条件多个属性时,SQL执行是否有排序过程
如果我们有联合索引idx_city_name,执行这个SQL的话,是不会走排序过程的,如下:
select * from staff where city in ("深圳") order by age limit 10;
但是,如果使用in条件,并且有多个条件时,就会有排序过程。
explain select * from staff where city in ("深圳","上海") order by age limit 10;
这是因为:in有两个条件,在满足深圳时,age是排好序的,但是把满足上海的age也加进来,就不能保证满足所有的age都是排好序的。因此需要Using filesort。
参考与感谢
MySQL实战45讲
推荐好文
>>【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能>>分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!>>能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!
标签:
相关推荐:
最新新闻:
- 全球百事通!格式化时间是什么?抽象类DateFormat是什么?
- 全球视点!FFT是什么意思?FFT的详解
- 格式化时间是什么?抽象类DateFormat是什么?:当前热文
- 迅雷怎么高速下载?迅雷高速下载方法
- 天天热点!.NET Core是什么?NET Core有哪些用处?
- 打印机共享需要密码怎么办?打印机共享需要密码解决方法
- 梯度(gradient)是什么?梯度的概念
- 如何解决手机浏览器中出现404notfound的问题?手机浏览器404notfound解决方法
- 天天快报!disable怎么运用?disable简单易理解用法
- 电脑无法进入系统怎么办?电脑无法进入系统解决方法
- Internal问题解决流程 Internal server error 500 问题解决思路-动态焦点
- 电脑的显示器屏幕老是闪烁怎么办?电脑的显示器屏幕老是闪烁解决方法
- 如何联系爱奇艺APP中的人工客服?爱奇艺APP人工客服联系步骤
- 宽带的内网和外网什么意思?宽带的内网和外网介绍
- USB2.0怎样提高传输速度?提高传输速度方法
- 摄像头远程监控步骤是什么?手机远程监控摄像头设置方法
- 多普达有哪些型号的手机?多普达最新手机大全详情 天天新视野
- U盘遇到病毒怎么办?U盘遇到病毒解决方法
- 快讯:文章目录是什么?选择排序怎么设置?
- 【天天速看料】Internal问题解决流程 Internal server error 500 问题解决思路
- 使用Icon图标的几种方式是啥?Icon图标怎么使用?
- 信号的基本概念是什么?信号的分类有哪些?
- java.lang.NullPointerException解决方案是什么?
- Excel函数之VLOOKUP()怎么使用?一文搞懂Excel函数之VLOOKUP()使用
- 倒车雷达哪个品牌好?汽车倒车雷达品牌及价格介绍_每日信息
- 美国多灵门锁怎么安装?美国多灵门锁安装步骤?
- 网络的利与弊是什么?网络的利弊分析:环球消息
- lol怎么在游戏里回复好友?lol游戏回复消息设置-环球视点
- 转动惯量是什么意思?转动惯量的含义
- OneNote是什么?OneNote的功能有哪些?
- 每日简讯:磁条读写器多少钱?磁条读写器怎么使用?
- 转动惯量是什么意思?转动惯量的含义 当前聚焦
- 三星N8010如何设置?三星N8010的屏幕锁定图案_快消息
- Modbus通信协议是什么?Modbus通信协议详解
- 世界热文:c语言餐桌游戏有哪些?教会你这十款酒桌游戏让你在朋友圈稳站“C”位!
- 电脑默认网关如何查询?电脑默认网关查询的小技巧
- iPad Air(iPad5)什么时候上市?iPad Air(iPad5)的上市时间-今日快看
- IE浏览器不见了怎么办?IE浏览器不见了解决方法
- 梯度(gradient)是什么?梯度的概念|天天日报
- 今亮点!JSONObject是什么意思?JSONObject详情介绍
- Win7专业版与Win7旗舰版如何区分?Win7专业版与Win7旗舰版区分方法
- 聚焦:工厂模式是什么?工厂模式的详解
- 世界今亮点!UML建模怎么用?UML建模之用例图
- 全球视讯!ThinkBook16p对比联想小新 Pro16 2021款哪个值得更好?详细评测
- Win7系统安装声卡驱动失败怎么办?声卡驱动安装失败解决方法
- mysql置疑原因是什么?数据库置疑的处理办法
- UML建模怎么用?UML建模之用例图:天天百事通
- order by是什么意思?order by详解
- 百度快照如何彻底删除?百度快照正确的删除方法
- 禁用宏则关闭excel文件怎么弄?设置流程_天天速递
- 电脑的显示器屏幕老是闪烁怎么办?电脑的显示器屏幕老是闪烁解决方法
- 如何联系爱奇艺APP中的人工客服?爱奇艺APP人工客服联系步骤
- 宽带的内网和外网什么意思?宽带的内网和外网介绍
- USB2.0怎样提高传输速度?提高传输速度方法
- U盘遇到病毒怎么办?U盘遇到病毒解决方法
- 语义分割:基于openCV和深度学习(二)_环球速看料
- 天天热点!专升本英语重要知识点补充 英语全部知识点详情介绍
- 【世界快播报】CSS:好玩的‘伪类’系列之——(:only-child与:only-of-type) 例子说明
- mysql置疑原因是什么?数据库置疑的处理办法
- 优麒麟社区懒人版本(含软件全家桶) 怎么一键安装?_当前速读
- 禁用宏则关闭excel文件怎么弄?设置流程_天天速递
- java中flypaper怎么使用?Java 基础接口fly_全球快播报
- 每日看点!用VC6.0怎么实现上位机串口通信?位机串口通信的设置方法
- 会议panel是什么意思?医学术语中的panel到底是指什么?
- 观点:颜料墨水和染料墨水的区别是什么?颜料墨水和染料墨水简介
- 天天速递!粘胶短纤维市场现状是什么?粘胶短纤维的未来发展趋势
- 浪漫主义时期的音乐有哪些?浪漫主义乐派是什么?-当前独家
- 世界热文:c语言餐桌游戏有哪些?教会你这十款酒桌游戏让你在朋友圈稳站“C”位!
- Android中LayoutParams是什么?Android中LayoutParams总结和用法
- 今日热门!数据结构试题有哪些?数据结构试题及评分解析
- ssm大学生兼职论坛是什么?大学生兼职有哪些?:每日观察
- 摄氏度和开氏度的换算 开氏度和摄氏度的换算公式 天天亮点
- 基础版本的基础版本 直方图均衡化系列
- 今日视点:怎么设置交换机?计算机交换机连接设置方法
- 天天要闻:图片或手写签名转电子签名怎么转?手写签名转电子签名教程
- 电脑默认网关如何查询?电脑默认网关查询的小技巧
- IE浏览器不见了怎么办?IE浏览器不见了解决方法
- Win7专业版与Win7旗舰版如何区分?Win7专业版与Win7旗舰版区分方法
- Win7系统安装声卡驱动失败怎么办?声卡驱动安装失败解决方法
- 百度快照如何彻底删除?百度快照正确的删除方法
- 英雄联盟无法全屏显示如何解决?英雄联盟无法全屏显示解决方法
- 如何解决IE浏览器网页图片显示红叉问题?IE浏览器网页图片显示红叉解决方法
- Win7系统安装CAD软件提示缺少dfst.dll怎么办?解决方法
- 内网端口映射怎么设置?内网端口映射定义及设置方法
- 美拍是什么?美拍怎么用?