在编写SQL 语句时常常会用到 order by 进行排序,那么排序过程是什么样的?为什么有些排序执行比较快,有些排序执行很慢?又该如何去优化?

索引排序

索引排序指的是在通过索引查询时就完成了排序,从而不需要再单独进行排序,效率高。索引排序是通过联合索引实现的。因为联合索引是从最左边的列开始起按大小顺序进行排序,如下图。

比如现在查询条件是 where sex=1 order by name,那么查询过程就是会找到满足 sex=1 的记录,而符合这条的所有记录一定是按照 name 排序的,所以也不需要额外进行排序。而如果是 where sex >1 order by name,那么根据 sex>1 得到的记录 sex 值并不是固定值,所以得到的记录是按照 sex,其次才是 name 进行排列的。也就没有实现索引排列。

额外排序

额外排序就是需要额外进行排序,也就是在 explain 的 extra 列中出现 using filesort。可以分别两种方式来看待。

按执行位置划分

1、Sort_Buffer

MySQL 为每个线程各维护了一块内存区域 sort_buffer ,用于进行排序。sort_buffer 的大小可以通过 sort_buffer_size 来设置。如果加载的记录字段总长度(可能是全字段也可能是 rowid排序的字段)小于 sort_buffer_size 便使用 sort_buffer 排序;如果超过则使用 sort_buffer + 临时文件进行排序。

2、Sort_Buffer + 临时文件

MySQL 会使用临时文件搭配 Sort_Buffer 进行排序。主要是使用归并算法来得出最终排序后的结果。

临时文件种类:

临时表种类由参数 tmp_table_size 与临时表大小决定,如果内存临时表大小超过 tmp_table_size ,那么就会转成磁盘临时表。因为磁盘临时表在磁盘上,所以使用内存临时表的效率是大于磁盘临时表的。

1、内存临时表

2、磁盘临时表磁盘临时表默认使用的是 InnoDB,如果想要切换执行引擎,可以修改参数internal_tmp_disk_storage_engine。

按执行方式划分

执行方式是由 max_length_for_sort_data 参数与用于排序的单条记录字段长度决定的,如果用于排序的单条记录字段长度 <= max_length_for_sort_data ,就使用全字段排序;反之则使用 rowid 排序。

1、全字段排序

全字段排序就是将查询的所有字段全部加载进来进行排序。

优点:查询快,执行过程简单

缺点:需要的空间大。

例子(不考虑临时文件):select city,name,age from t where city='杭州' order by name limit 1000  ;    city 有索引

1、初始化 sort_buffer,确定放入两个字段,即 name 和 id;

2、从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;

3、到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;

4、从索引 city 取下一个记录的主键 id;

5、重复步骤 3、4 直到不满足 city='杭州’条件为止,也就是图中的 ID_Y;

6、对 sort_buffer 中的数据按照字段 name 进行排序;

7、遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。

2、rowid 排序

rowid 表示位置信息,如果整张表有主键那么 rowid 就是主键,如果没有主键就会自动创建一个 6 字节的唯一标识。所以 rowid 排序就表示只加载用于排序的字段以及 rowid ,然后进行排序,然后根据排序好的 rowid 去表中回表查询所要的结果。

缺点:会产生更多次数的回表查询,查询可能会慢一些。

优点:所需的空间更小。

例子(不考虑临时文件):select city,name,age from t where city='杭州' order by name limit 1000  ;    city 有索引

1、初始化 sort_buffer,确定放入两个字段,即 name 和 id;

2、从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;

3、到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;

4、从索引 city 取下一个记录的主键 id;

5、重复步骤 3、4 直到不满足 city='杭州’条件为止,也就是图中的 ID_Y;

6、对 sort_buffer 中的数据按照字段 name 进行排序;

7、遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。

执行案例分析

rand() 执行

select word from words order by rand() limit 3;    表数据有10000行      SQL是从10000行记录中随机获取3条记录返回。

分析: 这里查询的字段只有一个,所以使用全字段查询。 加上记录数过多,但是单条记录的字段长度不长,所以会使用 sort_buffer + 内存临时表。所以总结来看这条语句会使用全字段查询 + sort_buffer + 内存临时表来排序。

执行过程:

1、从缓冲池依次读取记录,每次读取后都调用 rand() 函数生成一个 0-1 的数存入内存临时表,W 是 word 值,R 是 rand() 生成的随机数。到这扫描了 10000 行。

2、初始化 sort_buffer,从内存临时表中将 rowid(这张表自动生成的) 以及 排序数据 R 存入 sort_buffer。到这因为要遍历内存临时表所以又扫描了 10000 行。

3、在 sort_buffer 中根据 R 排好序,然后选择前三个记录的 rowid 逐条去内存临时表中查到 word 值返回。到这因为取了三个数据去内存临时表去查找所以又扫描了 3 行。总共 20003 行。

rand() 优化

通过上面的例子可以看出当要从表中随机获取几条记录使用 rand() 函数是非常消耗资源的,同时触发了 Using temporary(使用了临时表) 和 Using filesort(使用了额外排序)。并且进行了 20003 行记录的扫描,非常消耗资源。所以我们可以自己去计算一个随机值,避免使用 rand() 函数。

查询随机的一条记录:

1、取得整个表的行数,并记为 C。

2、取得 Y = floor(C * rand())。floor 函数在这里的作用,就是取整数部分。

3、再用 limit Y,1 取得一行。

select count(*) into @C from t;

set @Y = floor(@C * rand());

set @sql = concat("select * from t limit ", @Y, ",1");

prepare stmt from @sql;

execute stmt;

DEALLOCATE prepare stmt;

如果查询多条,只要将第二步执行多次,然后依次执行就可以了。

使用这样的方式就可以避免 MySQL 去使用临时表以及 filesort 排序,提高执行效率。

优先队列排序算法

在 5.6 中对排序算法进行一些优化,之前使用的是搭配临时表使用 归并排序算法,这种方式会对所有的记录都进行排序,消耗了不必要的资源例如有一个 20000 行记录的表,执行  select word from words order by rand() limit 3;

因为这条语句只取三条记录,对这剩余的 19993 行进行排序比较浪费CPU资源且耗时。所以在 5.6 中提出了使用 优先队列排序算法。还是以这个例子为例,因为查询的字段只有一个,且查询的行数很多,所以还是使用 全字段查询 + sort_buffer + 内存临时表 。

过程:先读取前三行记录并为其分别通过 rand() 函数为其设置一个0-1的随机数,取这三条记录的 rowid、随机数组成一个堆,然后依次设置随机数并与当前堆中的随机数比较。如果这个随机数比堆中某个记录的随机数小,就替换,然后移除,如果没有小的就直接移除,取下一个。最后根据堆中的 rowid 去临时表中读取对应的 word 值返回。

失效场景:因为要拿指定的记录数的排序数据以及rowid去挨个比较,所以如果需要返回的记录数过多,导致所有的字段长度超过了设置的 sort_buffer_size ,那么此算法就会失效。

索引排序案例

问题:有 (city,name) 联合索引,select * from t where city in (“杭州”," 苏州 ") order by name limit 100; 这个 SQL 语句是否需要排序?有什么方案可以避免排序?

答案:需要排序。因为city 的条件有两个,总体上来看就是以 city优先进行排序的。可以优化成下面三步:

1、执行 select * from t where city=“杭州” order by name limit 100; 这个语句是不需要排序的,客户端用一个长度为 100 的内存数组 A 保存结果。

2、执行 select * from t where city=“苏州” order by name limit 100; 用相同的方法,假设结果被存进了内存数组 B。

3、现在 A 和 B 是两个有序数组,然后你可以用归并排序的思想,得到 name 最小的前 100 值,就是我们需要的结果了。

如果将 " limit 100" 改成 " limit 10000,100 "。可以优化成下面三步:

1、select id,name from t where city="杭州" order by name limit 10100;

2、select id,name from t where city="苏州" order by name limit 10100。

3、用归并排序的方法取得按 name 顺序第 10001~10100 的 name、id 的值,然后拿着这 100 个 id 到数据库中去查出所有记录。

优化总结

优化总体上就是围绕 “尽量不使用额外排序,避免使用临时表” 的原则。

1、尽量使用索引完成排序,如果该查询语句执行的频率比较高,可以为其创建一个联合索引。而如果使用的频率很低,那么就不需要去创建,因为索引的维护需要成本。

2、如果需要额外去排序,那么可以适当调整 sort_buffer_size(sort_buffer) 和 tmp_table_size(内存临时表) ,使排序只在内存中执行。

3、如果 sort_buffer 空间设置足够大,也可以适当调整 max_length_for_sort_data 的值,使用全字段排序。

4、对于一些比较耗时的函数可以自定义算法去实现,避免计算过程在 MySQL 中实现。

mysql排序1 10 11_MySQL中的排序相关推荐

  1. mysql自定义函数分组_mysql中的排序和分组及分组函数

    现实中有很多这样的需求,对全班同学成绩进行排名,将所有的学生信息按照班级来分组,先将学生按班级分组再进行成绩排名等等.今天老韩就来介绍一下如何通过数据库的排序.分组.分组排序来实现这个需求(是排序,不 ...

  2. python中的sort排序加换行_python中sort()排序的方法

    python中sort()排序的方法 发布时间:2020-09-01 10:57:52 来源:亿速云 阅读:110 作者:小新 这篇文章主要介绍了python中sort()排序的方法,具有一定借鉴价值 ...

  3. 如何自定义排序 aAbBcC 即Excel中的排序,AaBbCc

    列举排序中的几种情况,然后逐个进行排序即可 const CHAR_CODE_a = 'a'.charCodeAt(0); // 97 const CHAR_CODE_A = 'A'.charCodeA ...

  4. python中排序的函数_Python中sorted()排序函数

    语法: sorted(iterable, key=None, reverse=False) *iterable:可迭代的对象 *key:用来排序的元素,可以通过函数指定 *reverse:排序规则,r ...

  5. python两个数组合并排序_合并Python中的排序数组

    假设我们有两个排序数组A和B.我们必须将它们合并并仅形成一个排序数组C.列表的大小可能不同. 例如,假设A = [1,2,4,7]和B = [1,3,4,5,6,8],则合并列表C将为[1,1,2,3 ...

  6. mysql distribute by_Hive学习(八) 排序:order by、sort by、distribute by、cluster by

    hive中有四种排序: 1.全局排序:order by 2.内部排序:sort by 3.分区排序:distribute by 4.组合排序:cluster by 全局排序:order by 说明:全 ...

  7. 计算机word排序在哪,word里的排序在哪里 word文档中排序在哪儿

    word文档中排序在工具栏的"开始"选项中. 查找及使用步骤: 1.打开一个需要排序的Word文档. 2.在页面上方的工具栏中找到下图做红色标注的排序图标. 3.点击排序图标后,进 ...

  8. mysql中拼音排序的方法_mysql的中文数据按拼音排序的2个方法

    客服那边需要我对一些酒店进行中文拼音排序,以前没有接触过,在php群里问了一些大牛..得到了2种答案,都可以.哈哈·~ 以下既是msyql 例子,表结构是utf-8的 方法一.复制代码 代码如下:SE ...

  9. 数据库中自定义排序规则,Mysql中自定义字段排序规则,Oracle中自定义字段排序规则,decode函数的用法,field函数的用法

    数据库中自定义排序 场景:有一张banner表,表中有一个status字段,有0, 1, 2三个状态位,我想要 1,0,2的自定义排序(这里是重点),然后再进行之上对sequence字段进行二次排序( ...

最新文章

  1. 使用RSA私钥或pfx私钥签名String
  2. php session函数
  3. Hinton神经网络公开课编程练习1 The perceptron learning algorithm
  4. 【明星自动大变脸】最新StarGAN对抗生成网络实现多领域图像变换(附代码)
  5. Python-各种结构解析以及生成器(列表解析,字典解析,集合解析,元组构成的生成器)
  6. 从vCenter 6.7升级到7.0
  7. 自己写的一个ffmpeg时间戳分析工具
  8. DIV CSS设计时IE6、IE7、FF 与兼容性有关的特性
  9. VB6 TCP通讯服务端、客户端源码
  10. 一款在线画图工具的使用(推荐)
  11. Java求101-200以内的质数
  12. 分库分表中间件常见方案对比分析
  13. 5分钟latex 使用入门写llncs论文
  14. 登顶多国免费榜,Zynga能否依靠超休闲游戏重回巨头行列
  15. Java中对两个对象进行比较大小
  16. 【NLP】Python NLTK获取文本语料和词汇资源
  17. 用R语言画相关性矩阵,带P值和*号
  18. 计算机视觉基础--边缘检测
  19. SQL注入入侵动网SQL
  20. 任意地址读写驱动提权(cred、VDSO、modprobe_path、core_pattern、修改内核指针提权)

热门文章

  1. mysql 判断表或字段存不存在
  2. lighttpd php7 源码安装,如何在CentOS 7上安装Lighttpd与PHP-FPM和MariaDB
  3. db2 日期英式写法_《学霸英语》16:美国人和英国人“表达日期”,差距竟然这么大!...
  4. 缓冲区溢出_了解Java缓冲池
  5. 如何搭建html运行环境,搭建基于express框架运行环境的方法步骤
  6. Apache Dubbo 高危漏洞通告
  7. Spring Cloud Feign如何实现JWT令牌中继以传递认证信息
  8. Windows 的这款工具,有时让我觉得 Mac 不是很香
  9. 阿里巴巴公布“云钉一体”战略:阿里云与钉钉全面融合
  10. 阿里云发布云电脑“无影”,「传统桌面云」市场将被颠覆?