MySql order by 是如何工作的

示例:

CREATE TABLE `t` (

`id` int(11) NOT NULL,

`city` varchar(16) NOT NULL,

`name` varchar(16) NOT NULL,

`age` int(11) NOT NULL,

`addr` varchar(128) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `city` (`city`)

) ENGINE=InnoDB;

假设有 4000行 city = ‘杭州’

复制代码

select city,name,age from t where city='杭州' order by name limit 1000 ;

在mysql中是如何执行的呢?

explain执行计划中extra:Using file sort表示的就是需要排序,mysql会给每一个线程分配一块内存用于排序,称为sort_buffer

image.png

1.初始化sort_butter,确定放入name, city-age这三个字段
2.从索引city中找到第一个满足city=杭州条件的主键ID
3.回表查出整行数据,存入sort_buffer中
4.从索引city取下一个记录的主键id;
5.重复步骤3 4步骤直到city的值不满足条件为止
6.对_buffer中的数据按照name做快速排序
7按照排序结果返回前1000行给客户端
复制代码

其中按照name排序这个操作可能在内存中,也可能发生在临时文件中,这取决于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;
复制代码

image.png

可以通过OPTIMIZER_TRACE的结果(number_of_tem_files= xxx)来看是否使用了临时文件

mysql 将排序的文件分为xxx份,一般使用归并排序,进行多文件的排序,之后再合并成一个有序的大文件

examined_rows = 4000代表有4000行数据参与排序

sort_mode 里面的 packed_additional_fields 的意思是,排序过程对字符串做了“紧凑”处理。即使 name 字段的定义是 varchar(16),在排序过程中还是要按照实际长度来分配空间的。

@b-@a = 4000(MyISAM) InnoDB会是4001

ROWID

上面那种算法面临一个很严重的问题 就是单行很大的话怎么办,这个时候可以通过修改

SET max_length_for_sort_data = 16; 单行数据总长度超过16就会发生以下步骤

image.png

1.初始化sort_buffer,确定只放入name,id

2.从索引city中找到第一个满足的行

3.到主键索引中找到该行数据取出name 和id 存入sort_buffer中

4.从索引city中继续取下一行

5.重复3,4步骤

6.对sort_buffer中数据按照name排序

7.遍历结果找出那1000行数据,再次回表查出数据

@b-@a = 5000 最后回表查还有1000
复制代码

sort_mode 变成了 ,表示参与排序的只有 name 和 id 这两个字段。

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

image.png

如何优化呢,排序是因为取出来的数据都是无序的,那我们先办法让它变成有序的是不是就可以了呢,应该有人已经想到了就是建立(city,name)的联合索引

1.从索引 (city,name) 找到第一个满足 city='杭州’条件的主键 id;

2.到主键 id 索引取出整行,取 name、city、age 三个字段的值,作为结果集的一部分直接返回;

3.从索引 (city,name) 取下一个记录主键 id;

4.重复步骤 2、3,直到查到第 1000 条记录,或者是不满足 city='杭州’条件时循环结束。

最后只需要扫描1000行就足够了

究极方案(city_name_age)联合索引 索引覆盖 Extra 字段里面多了“Using index”,表示的就是使用了覆盖索引,性能上会快很多。

image.png

猜你喜欢

转载自juejin.im/post/7039945787047936008
今日推荐