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 的一些内置特殊配置时环境变量和命令行这两个提供程序在早期是没有容器化的&…

python和qt哪个好_做个小的桌面用node还是QT比较好? - Web开发 - WebDev - 水木社区...

楼主说了自己水平不高,那就专注 py 一门语言就好了。electron 这边虽然现在已经简单了很多,但也是两个进程起步,main 和 renderer 进程的 api 又不一样,renderer 只有 node api 的子集。你大概觉得不挺简单的么,但对于…

堆元素插入 二叉堆一般用数组来表示。typedef struct _otherInfo{ int i; int j;}OtherInfo;-icoding-C-数据结构

堆元素插入 二叉堆一般用数组来表示。例如,根节点在数组中的位置是0,第n个位置的子节点分别在2n1和 2n2。 因此,第0个位置的子节点在1和2,1的子节点在3和4。以此类推。这种存储方式便于寻找父节点和子节点。 在二叉堆上可以进行插…

dotnetcore3.1 WPF 实现多语言

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

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

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

堆化 二叉堆一般用数组来表示。typedef struct _minHeapNodetypedef struct _otherInfo-icoding-C-数据结构

堆化 二叉堆一般用数组来表示。例如,根节点在数组中的位置是0,第n个位置的子节点分别在2n1和 2n2。 因此,第0个位置的子节点在1和2,1的子节点在3和4。以此类推。这种存储方式便于寻找父节点和子节点。 在二叉堆上可以进行插入节点…

POJ 3984 迷宫问题

定义一个二维数组: int maze[5][5] { 0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0,}; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要…

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

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

python升级pip在哪儿打开_Linux下升级python和安装pip的详解

Linux版本升级:1、首先确认Linux操作系统中自带的python 版本时候与自己所需要的版本一致3、解压tar -zxvf Python-2.7.11.tgz进入Python-2.7.11目录 输入./configuremakemake install4、此时查看python版本还是系统默认的版本执行:mv /usr/bin/python …

[蓝桥杯2017决赛]图书排列-next_permutation枚举

题目描述 将编号为1~10的10本书排放在书架上&#xff0c;要求编号相邻的书不能放在相邻的位置。 请计算一共有多少种不同的排列方案。 输出 输出一个整数表示答案 代码如下: #include <iostream> #include <algorithm> using namespace std;int a[] {1, 2, 3, 4…

第一个一千行总结-数据结构C复习--知识点总结1--一到四章

总结 第一章: 数据结构包括:逻辑结构,储存结构, 运算集合 逻辑结构:分为线性(线性表, 栈, 队列, 字符串, 数组, 广义表) 非线性:树,图,网 储存结构:顺序储存和非顺序储存 线性储存,散列储存,链式储存 算法数据结构 程序 第二章:线性表: 线性表的线性储存 逻辑结构:…

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

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

[蓝桥杯2016决赛]阶乘位数-数论

题目描述 9的阶乘等于&#xff1a;362880 它的二进制表示为&#xff1a;1011000100110000000 这个数字共有19位。 请你计算&#xff0c;9999 的阶乘的二进制表示一共有多少位&#xff1f; 输出 输出一个整数表示答案 知识点: 整数m在k进制下&#xff0c;有多少位&#xff1f; …

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

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

数组合并假设有 n 个长度为 k 的已排好序(升序)的数组,请设计数据结构和算法,将这 n 个数组合并到一个数组,且各元素按升序排列。即实现函数-C-icoding-排序-数据结构

数组合并 假设有 n 个长度为 k 的已排好序&#xff08;升序&#xff09;的数组&#xff0c;请设计数据结构和算法&#xff0c; 将这 n 个数组合并到一个数组&#xff0c;且各元素按升序排列。即实现函数&#xff1a; void merge_arrays(const int* arr, int n, int k, int* out…

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

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

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

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