mysql查询语句能否让一个字段不显示出来_天天写order by,你知道Mysql底层如何执行吗?

作者:不才陈某

前言

在实际的开发中一定会碰到根据某个字段进行排序后来显示结果的需求,但是你真的理解order by在 Mysql 底层是如何执行的吗?假设你要查询城市是苏州的所有人名字,并且按照姓名进行排序返回前 1000 个人的姓名、年龄,这条 sql 语句应该如何写?首先创建一张用户表,sql 语句如下:
CREATE TABLE user (  id int(11) NOT NULL,  city varchar(16) NOT NULL,  name varchar(16) NOT NULL,  age int(11) NOT NULL,  PRIMARY KEY (id),  KEY city (city)) ENGINE=InnoDB;
  • 则上述需求的 sql 查询语句如下:
select city,name,age from user where city='苏州' order by name limit 1000;
  • 这条 sql 查询语句相信大家都能写出来,但是你了解它在 Mysql 底层的执行流程吗?今天陈某来大家聊一聊这条 sql 语句是如何执行的以及有什么参数会影响执行的流程。
  • 本篇文章分为如下几个部分进行详细的阐述:全字段排序rowid 排序全字段排序 VS rowid 排序如何避免排序

全字段排序

  • 前面聊过索引能够避免全表扫描,因此我们给city这个字段上添加了索引,当然城市的字段很小,不用考虑字符串的索引问题.
  • 此时用Explain来分析一下的这条查询语句的执行情况,结果如下图:
d93d0c15f2f87c11ea31857cf5c9af70.png
  • Extra这个字段中的Using filesort表示的就是需要排序,MySQL 会给每个线程分配一块内存用于排序,称为sort_buffer。
  • 既然使用了索引进行查询,我们来简单的画一下city这棵索引树的结构,如下图:
e3bc8cd18c2e4bf9c974b6a1d80a7807.png
  • 从上图可以看出,满足city='苏州'是从ID3到IDX这些记录。
  • 通常情况下,此条 sql 语句执行流程如下:
  1. 初始化 sort_buffer,确定放入 name、city、age 这三个字段。
  2. 从索引 city 找到第一个满足city='苏州'条件的主键id,也就是图中的ID3。
  3. 到主键id索引取出整行,取name、city、age三个字段的值,存入sort_buffer中。
  4. 从索引city取下一个记录的主键 id。
  5. 重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的IDX。
  6. 对sort_buffer中的数据按照字段name做快速排序。
  7. 按照排序结果取前 1000 行返回给客户端。
  • 我们称这个排序过程为全字段排序,执行的流程图如下:
87328af9e3d5b0e2cc2428edb419bce9.png
  • 图中按name排序这个动作,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数sort_buffer_size。
  • sort_buffer_size:就是 MySQL 为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。

rowid 排序

  • 在上面这个算法过程里面,只对原表的数据读了一遍,剩下的操作都是在sort_buffer和临时文件中执行的。但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么sort_buffer里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差
  • 所以如果单行很大,这个方法效率不够好。
  • 我们可以修改一个max_length_for_sort_data这个参数使其使用另外一种算法。max_length_for_sort_data,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。
  • city、name、age 这三个字段的定义总长度是36,我把max_length_for_sort_data设置为 16,我们再来看看计算过程有什么改变。设置的 sql 语句如下:
SET max_length_for_sort_data = 16;
  • 新的算法放入 sort_buffer 的字段,只有要排序的列(即 name 字段)和主键 id。
  • 但这时,排序的结果就因为少了 city 和 age 字段的值,不能直接返回了,整个执行流程就变成如下所示的样子:
  1. 初始化sort_buffer,确定放入两个字段,即name和id。
  2. 从索引 city 找到第一个满足city='苏州'条件的主键id,也就是图中的ID3。
  3. 到主键id索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中。从索引city取下一个记录的主键 id。
  4. 重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的IDX。
  5. 对sort_buffer中的数据按照字段name做快速排序。
  6. 遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。
  7. 这个执行流程的示意图如下,我把它称为rowid排序。
a3d4e30d71cc2eb6d252f56216398de6.png
  • 对比全字段排序,rowid排序多了一次回表查询,即是多了第7步的查询主键索引树。

全字段排序 VS rowid 排序

  • 如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。
  • 如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer 中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。
  • 这也就体现了 MySQL 的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问
  • 对于 InnoDB 表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择。

如何避免排序

  • 其实,并不是所有的order by语句,都需要排序操作的。从上面分析的执行过程,我们可以看到,MySQL 之所以需要生成临时表,并且在临时表上做排序操作,其原因是原来的数据都是无序的
  • 如果能够保证从city这个索引上取出来的行,天然就是按照 name 递增排序的话,是不是就可以不用再排序了呢?
  • 因此想到了联合索引,创建(city,name)联合索引,sql 语句如下:
alter table user add index city_user(city, name);
  • 此时的索引树如下:
c995f5fc234c216b6cd12c75e12923a6.png
  • 在这个索引里面,我们依然可以用树搜索的方式定位到第一个满足city='苏州'的记录,并且额外确保了,接下来按顺序取“下一条记录”的遍历过程中,只要 city 的值是杭州,name 的值就一定是有序的。
  • 按照上图,整个查询的流程如下:从索引(city,name)找到第一个满足 city='苏州’条件的主键 id。到主键 id 索引取出整行,取 name、city、age 三个字段的值,作为结果集的一部分直接返回。从索引(city,name)取下一个记录主键 id。重复步骤 2、3,直到查到第 1000 条记录,或者是不满足 city='苏州'条件时循环结束。
  • 对应的流程图如下:
f0b13530dc93018f6805efd7865d3bde.png
  • 可以看到,这个查询过程不需要临时表,也不需要排序。接下来,我们用 explain 的结果来印证一下。
    image
  • 从图中可以看到,Extra字段中没有Using filesort了,也就是不需要排序了。而且由于(city,name)这个联合索引本身有序,所以这个查询也不用把 4000 行全都读一遍,只要找到满足条件的前 1000 条记录就可以退出了。也就是说,在我们这个例子里,只需要扫描 1000 次。
  • 难道仅仅这样就能满足了?此条查询语句是否能再优化呢?
e7ebb442edc6bd8249b53a96f86208ce.png
  • 朋友们还记得覆盖索引吗?覆盖索引的好处就是能够避免再次回表查询。
  • 我们创建(city,name,age)联合索引,这样在执行上面的查询语句就能使用覆盖索引了,避免了回表查询了,sql 语句如下:
讲真,这两款idea插件,能治愈你英语不好的病alter table user add index city_user_age(city, name, age);
  • 此时执行流程图如下:
58f801fbb4befedeb73382d1b3288691.png
  • 当然,覆盖索引能够提升效率,但是维护索引也是需要代价的,因此还需要权衡使用。

总结

  • 今天这篇文章,我和你介绍了 MySQL 里面order by语句的几种算法流程。
  • 在开发系统的时候,你总是不可避免地会使用到 order by 语句。心里要清楚每个语句的排序逻辑是怎么实现的,还要能够分析出在最坏情况下,每个语句的执行对系统资源的消耗,这样才能做到下笔如有神,不犯低级错误。

来源:掘金 链接:https://juejin.im/post/5e945b9651882573b7537c2a

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

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

相关文章

排序-总结

排序 排序:重点在于对于记录的关键字进行排序,得到按关键字有序记录序列 分为: A.内部排序: 排序过程在内存中进行 B.外部排序: 待排序记录数据量过大,需要借助外部存储设备 排序的稳定性:排序后有相同关键字的记录顺序不变就是稳定的排序 插入类排序: 1.直接插入排…

.NET Core开发实战(第10课:环境变量配置提供程序)--学习笔记

10 | 环境变量配置提供程序:容器环境下配置注入的最佳途径环境变量的配置提供程序主要适应场景:1、在 Docker 中运行时2、在 Kubernetes 中运行时3、需要设置 ASP.NET Core 的一些内置特殊配置时环境变量和命令行这两个提供程序在早期是没有容器化的&…

dotnetcore3.1 WPF 实现多语言

dotnetcore3.1 WPF 实现多语言Intro最近把 DbTool 从 WinForm 迁移到了 WPF,并更新到了 dotnet core 3.1,并实现了基于 Microsoft.Extensions.Localization 实现了基本的多语言支持。下面来分享一下如何来实现服务注册如果不熟悉如何在 WPF 中使用依赖注…

卸载chrome_Chrome 浏览器必备“扩展管理工具”,一键管理 Chrome 扩展

前言丰富的扩展插件可以说是 Chrome 浏览器的灵魂了,但是扩展安装的多了,难免会引起卡顿,而且每次打开/关闭扩展都要进入扩展程序页面,切换起来很不方便。下面分享的三款 Chrome 扩展管理工具,可以让我们更便捷地管理 …

面试官:你连HTTP请求Post和Get都不了解?

IT界知名的程序员曾说:对于那些月薪三万以下,自称IT工程师的码农们,其实我们从来没有把他们归为我们IT工程师的队伍。他们虽然总是以IT工程师自居,但只是他们一厢情愿罢了。此话一出,不知激起了多少(码农)程序员的愤怒…

运维进化论:微盟“删库跑路”给我们的启示

作者:茹炳晟,软件质量和研发工程效能专家事件背景微盟是国内移动互联网营销引领者,中国最大的微信公众智能服务平台,基于微信为企业提供开发、运营、培训、推广一体化解决方案,帮助企业实现线上线下互通,社…

拦截器如何获取@requestbody_分布式系统中如何优雅地追踪日志(原理篇)

分布式系统中日志追踪需要考虑的几个点?需要一个全服务唯一的id,即traceId,如何保证?traceId如何在服务间传递?traceId如何在服务内部传递?traceId如何在多线程中传递?我们一一来解答&#xff1…

.NET Core开发实战(第11课:文件配置提供程序)--学习笔记

11 | 文件配置提供程序:自由选择配置的格式文件配置提供程序Microsoft.Extensions.Configuration.IniMicrosoft.Extensions.Configuration.JsonMicrosoft.Extensions.Configuration.NewtonsoftJsonMicrosoft.Extensions.Configuration.XmlMicrosoft.Extensions.Conf…

前端demo_【前端3分钟】Script Error产生的原因和解法

Script Error对于前端开发者相信都不陌生,而且由于没有具体错误堆栈和代码行列号,成为可能是最神秘的错误之一。下面介绍Script Error产生的原理和解决办法。1、Script Error是如何产生的跨域资源引用假如:abc.com 下的页面引用了属于 http:/…

基于Abp VNext框架设计 - Masstransit分布式消息

abp 通过IDistributedEventBus接口集成自IEventBus实现分布式事件消息的发布订阅。IEventBus在什么时机触发PublishAsync?当前UnitOfWork完成时,触发IEventBus的PublishAsync在没有事务环境下,同步调用IEventBus的PublishAsyncabp 默认实现基于RabbitMq…

16进制数用空格分开 tcp_面试时,你是否被问到过TCP/IP协议?

点击蓝字关注我们看到这句话,有没有感到很熟悉呀?相信很多人在面试的时候都被要求,很多人会觉得我们在实际开发中一般用不到这些知识,所以对这些东西不屑一顾。但是小编认为想要成为一个完美的网工,那么对这些基础知识必须要有一定…

直接使用汇编编写 .NET Standard 库

前言Common Language Runtime(CLR)是一个很强大的运行时,它接收 Common Intermediate Language(CIL) 的输入并最终产生机器代码并执行。CIL 在 CLR 上相当于 ASM 汇编代码的存在。CLR 之上的语言 C#、F#、VB.NET 等语言…

[蓝桥杯2016决赛]七星填数-next_permutation枚举

题目描述 如下图所示: 在七角星的14个节点上填入1~14 的数字,不重复,不遗漏。要求每条直线上的四个数字之和必须相等。 图中已经给出了3个数字。请计算其它位置要填充的数字,答案唯一。 填好后,请提交绿色节点的4个数…

系统蓝屏的几种姿势,确定不了解下么?

前言在 蓝屏(BSOD)转储设置,看本文就够了!这篇文章里比较详细的介绍了蓝屏转储设置。做好设置后,我们就可以在需要的时候使系统蓝屏了。本文介绍几种使系统蓝屏的办法,当然肯定还有其它办法,如果…

最长公共子串-dp

题目: 给定两个字符串,求出它们之间最长的相同子字符串的长度。 公共子串和公共子序列不同,公共子序列不要求连续,但公共子串必须是连续的。如: A “helloworld” B “loop” A和B的最长公共子序列是"loo",但最长公共子串是&quo…

智能对话引擎:两天快速打造疫情问答机器人

01微软AI技术开源知识库疫情机器人近一个月来,“新冠肺炎疫情”成了所有人的热点话题,抗击疫情的战役在全国紧张有序地进行着。随着全国各地的企业陆续复工,怎样防范、保护自己和家人成了当下每个人的焦点。为了配合奋战在一线的医护人员打赢…

数码管

题目背景 小明的单片机上面的LED显示屏坏掉了,于是他请你来为他修显示屏。 屏幕上可以显示0~9的数字,其中每个数字由7个小二极管组成,各个数字对应的表示方式如图所示: 题目描述 为了排除电路故障,现在你需要计算&am…

fh 幅频特性曲线怎么画fl_初学者怎么练习线条?教你如何画出流畅线条的技巧...

初学者怎么练习线条?怎样才能画出流畅线条?画出流畅线条有哪些技巧?想必这些问题都是绘画初学者们比较伤脑筋的问题,那么到底怎样才能画出流畅线条呢?今天灵猫课堂老师就在网络上收集整理了关于初学者怎么练习线条&…