10.order by是怎么工作的?

10.order by是怎么工作的?

img

MySQL 会给每个线程分配一块内存用于排序,称为 sort_buffer。

初始化 sort_buffer,确定放入 name、city、age 这三个字段;

  1. 从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;到主键 id 索引取出整行,取 name、city、age 三个字段的值,存入 sort_buffer 中;

  2. 从索引 city 取下一个记录的主键 id;

  3. 重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的 ID_Y

  4. 对 sort_buffer 中的数据按照字段 name 做快速排序;

  5. 按照排序结果取前 1000 行返回给客户端。

图中“按 name 排序”这个动作,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数 sort_buffer_size。sort_buffer_size,就是 MySQL 为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。

/* 打开optimizer_trace,只对本线程有效 */
SET optimizer_trace='enabled=on'; 

/* @a保存Innodb_rows_read的初始值 */
select VARIABLE_VALUE into @a from  performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 执行语句 */
select city, name,age from t where city='杭州' order by name limit 1000; 

/* 查看 OPTIMIZER_TRACE 输出 */
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G

/* @b保存Innodb_rows_read的当前值 */
select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 计算Innodb_rows_read差值 */
select @b-@a;

内存放不下时,就需要使用外部排序,外部排序一般使用归并排序算法

可以这么简单理解,MySQL 将需要排序的数据分成 12 份,每一份单独排序后存在这些临时文件中。然后把这 12 个有序文件再合并成一个有序的大文件。

在上面这个算法过程里面,只对原表的数据读了一遍,剩下的操作都是在 sort_buffer 和临时文件中执行的。但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么 sort_buffer 里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。所以如果单行很大,这个方法效率不够好。那么,如果 MySQL 认为排序的单行长度太大会怎么做呢?接下来,我来修改一个参数,让 MySQL 采用另外一种算法。

rowid排序

  • 从 OPTIMIZER_TRACE 的结果中,你还能看到另外两个信息也变了。sort_mode 变成了 ,表示参与排序的只有 name 和 id 这两个字段。

  • number_of_tmp_files 变成 10 了,是因为这时候参与排序的行数虽然仍然是 4000 行,但是每一行都变小了,因此需要排序的总数据量就变小了,需要的临时文件也相应地变少了。

全字段排序 VS rowid 排序我们来分析一下,从这两个执行流程里,还能得出什么结论。如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer 中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。这也就体现了 MySQL 的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问

并不是所有的 order by 语句,都需要排序操作的。从上面分析的执行过程,我们可以看到,MySQL 之所以需要生成临时表,并且在临时表上做排序操作,其原因是原来的数据都是无序的。你可以设想下,如果能够保证从 city 这个索引上取出来的行,天然就是按照 name 递增排序的话,是不是就可以不用再排序了呢?确实是这样的。所以,我们可以在这个市民表上创建一个 city 和 name 的联合索引

posted @ 2020-08-02 21:29  wswgot  阅读(75)  评论(0编辑  收藏  举报