实习生疑问:为什么要在需要排序的字段上加索引呢?
hello,大家好,我是张张,「架构精进之路」公号作者。
排序有好多种算法来实现,在 MySQL 中经常会带上一个 limit,表示从排序后的结果集中取前 100 条,或者取第 n 条到第 m 条。
要实现排序,我们需要先根据查询条件获取结果集,然后在内存中对这个结果集进行排序,如果结果集数量特别大,还需要将结果集写入到多个文件里,然后单独对每个文件里的数据进行排序,然后在文件之间进行归并,排序完成后在进行 limit 操作。
没错,这个就是 MySQL 实现排序的方式,前提是排序的字段没有索引。
建表操作
CREATE TABLE `person` (
`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;
查询示例
select city,name,age from person where city='南京' order by name limit 100 ;
使用 explain 发现该语句会使用 city 索引,并且会有 filesort。
我们分析下该语句的执行流程:
1.初始化 sortbuffer,用来存放结果集;
2.找到 city 索引,定位到 city 等于南京的第一条记录,获取主键索引ID;
3.根据 ID 去主键索引上找到对应记录,取出 city,name,age 字段放入 sortbuffer;
4.在 city 索引取下一个 city 等于南京的记录的主键ID;
5.重复上面的步骤,直到所有 city 等于南京的记录都放入 sortbuffer;
6.对 sortbuffer 里的数据根据 name 做快速排序;
7.根据排序结果取前面 1000 条返回。
另外如果 sortbuffer 里的条数很多,同样会占有大量的内存空间,可以通过参数 sort_buffer_size 来控制是否需要借助文件进行排序,这里会把 sortbuffer 里的数据放入多个文件里,用归并排序的思路最终输出一个大的文件。
关于sortbuffer,官方文档:dev.mysql.com/doc/refman/…
以上方案主要是 name 字段没有加上索引,如果 name 字段上有索引,由于索引在构建的时候已经是有序的了,所以就不需要进行额外的排序流程只需要在查询的时候查出指定的条数就可以了,这将大大提升查询速度。我们现在加一个 city 和 name 的联合索引。
alter table person add index city_user(city, name);
使用 explain 发现该语句会使用 city_user 索引,并且没有了 filesort。
1.根据 city,name 联合索引定位到 city 等于武汉的第一条记录,获取主键索引ID
2.根据 ID 去主键索引上找到对应记录,取出 city,name,age 字段作为结果集返回
3.继续重复以上步骤直到 city 不等于武汉,或者条数大于 1000
Backward index scan:使用索引扫描。索引本身就是有序的,所以不需要再次进行排序
using filessort:在内存中排序,占用CPU资源。如果查询结果太大还会产生临时文件,到磁盘中进行排序,这时候会进行大量IO操作性能较差
其实这个SQL是分三步来执行的:
where得到数据;
排序处理数据首先看执行计划是不是用到索引,如果用到了就可以直接获得索引的顺序,从而避免再次排序,如果没用到就做排序(using filessort);
返回数据。
总结
最后,简单总结一下:
Order By语句跟WHERE语句中都用了索引字段,Order By中的索引才会生效
Order By中使用索引可避免重新排序导致CPU资源浪费
如果您觉得还不错,欢迎关注和转发~