思考
1.order by是如何实现的呢?
实践1
创建表
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;
插叙city是杭州的数据
select city,name,age from t where city='杭州' order by name limit 10 ;
全字段排序
explain select city,name,age from t where city='杭州' order by name limit 1000 ;
city上加了索引
其中extra这个字段中有"Using filesort"就是需要排序的意思,Mysql会给每个线程分一块内存用于排序,叫做sort buffer
1.初始化 sort_buffer,确定放入 name、city、age 这三个字段;
2.从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;
3.到主键 id 索引取出整行,取 name、city、age 三个字段的值,存入 sort_buffer 中;
4.从索引 city 取下一个记录的主键 id;
5.重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的 ID_Y;
6.对 sort_buffer 中的数据按照字段 name 做快速排序;
7.按照排序结果取前 1000 行返回给客户端。
可见全字段排序,会将所有字段放进sort_buffer.如果按照name排序,可能在内存中完成,也可能在外部排序,这取决于sort_buffer_size
sort_buffer_size,就是 MySQL 为排序开辟的内存(sort_buffer)的大小。
如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。
但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。
内存放不下就会使用归并排序,Mysql将需要排序的数据分为n份,分区治理,最后合并再次排序,当number_of_tmp_files为0的时候排序就可以在内存中完成了。
row_id排序
1.上面算法出现的问题,因为只对原表数据读一遍,剩下操作交给sort_buffer和临时文件中进行,但这个算法有一个问题,如果查询要返回字段很多,sort_buffer里面放的字段太多,这样内存能够放下的行数很少,要分成多个临时文件,排序性能很差。
(单行很多数据,也要分治?)
2.max_length_for_sort_data是Mysql专门控制行数据的一个参数,当单行长度超过这个值,换一种算法。
长度为16+16+4 = 32
新的算法:
初始化 sort_buffer,确定放入两个字段,即 name 和 id;
从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;
到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;
从索引 city 取下一个记录的主键 id;重复步骤 3、4 直到不满足 city='杭州’条件为止,也就是图中的 ID_Y;
对 sort_buffer 中的数据按照字段 name 进行排序;
遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。
查询的结果集是个逻辑概念,也就是查询出来的数据还是回表查询1000条
但是排序结果行数为5000行,因为排序行数是4000 + 1000(1000)
全字段排序VS rowid排序
1.全字段排序大量应用内存,所以会被优先考虑。
2.rowid排序会减少内存消耗,但是会造成磁盘回表,所以不会优先考虑。
3.Mysql的特点就是,优先使用内存。
排序优化
在表中创建联合索引
city,name
再次分析查询结果
explain select city,name,age from t force index (city) where city='杭州' order by name limit 1000 ;
发现没有了Using filesort 了,也就是不需要排序了,这个联合索引本身就是有序的。
再使用覆盖索引
explain select city,name,age from t force index (city) where city='杭州' order by name limit 5 ;
查询杭州,苏州的数据
explain select city,name,age from t force index(city) where city in ('杭州',"苏州") order by name limit 2;