假设我们要查询一个市民表中城市=杭州的所有人的名字,并且按照名字排序
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;
那么sql语句可以这样写
select city,name,age from t where city='杭州' order by name limit 1000 ;
接下来我们看下explain的结果
图中的Extra这一列下面的Using filesort表示需要排序,MySQL会为每个连接分配一块内存用于排序,就是sort_buffer,sort_buffer_size可以调整该排序内存大小
因为我们where条件用到了city,所以我们在city上面建立了索引
我们先看下该索引结构
从图中可以看出满足city=杭州的条件是ID_X到ID_Y之间的数据
通常情况下这个语句的执行流程如下:
1.初始化sort_buffer,确定放入name,age,city三个字段
2.从索引city中找到第一个符合条件的数据,也就是ID_X这个
3.取出索引中id的值,回表查询name,age,city的数据放入sort_buffer中
4.从索引city取下一个符合条件的id
5.重复步骤3,4直到city的值不满足city=杭州的条件,也就是图中ID_Y
6.对sort_buffer中的数据按照name排序
7.按照排序结果取前1000行数据返回给客户端
我们把这个排序过程叫全字段排序
如下图所示
上图按name排序这个动作可能在内存中完成也可能需要外部排序,这取决于排序需要的内存大小和sort_buffer_size这个参数
如果排序需要的内存大于sort_buffer_size设置的数值,那么就需要使用磁盘临时文件辅助排序
rowid排序
在上面的那个全字段排序中,只对原表查询了一次,但是如果查询的字段很多的话,那么sort_buffer中就会很多数据,就会使用到
磁盘临时辅助文件排序,这样性能会变差。
那么如果mysql认为单行数据过大会怎么办呢?
接下来设置一下这个参数为16
max_length_for_sort_data这个参数是mysql专门用来控制用于排序的行数据的单行的长度的一个参数,如果单行数据的字段的长度超过这个参数设置的值
那么就会使用rowid排序,比如说我们这个例子中name,age,city这三个字段的单行数据长度之和要是大于16,那么就会使用rowid排序
排序流程:
1.初始化sort_buffer,确定放入id,name
2.取出city索引中第一个满足条件的索引的id值
3.到主键id索引里面取出整行,取出name,id字段放入sort_buffer
4.去下一个符合条件的索引记录,放入sort_buffer中
5.重复步骤3.4直到不满足city=杭州
6.对sort_buffer中的数据按照name进行排序
7.遍历排序结果取出前1000行的数据的id,去表中查询出name,age,city返回给客户端
可以看出来rowid排序比全字段排序多了一次表查询就是步骤7
流程如下图所示