select city,name,age from t where city = '上海' order by name limit 1000;
explain select city,name,age from t where city = '上海' order by name limit 1000;
查看sql语句的执行情况,Extra字段中的"Using filesort"表示需要排序,MySQL会给每个线程分配一块内存用于排序,称为sort_buffer。
该语句的执行过程:
按name排序这个动作,可能在内存中完成,也可能需要使用外部排序,取决于排序内所需的内存和参数sort_buffer_size,排序的数据量小于sort_buffer_size就在内存中完成,如果数据量太大,就得用磁盘临时文件辅助排序。
如果查询要返回的字段很多,sort_buffer里面存放的字段数太多,内存里同时放下的行数很少,要分成很多文件,排序的性能会很差。
MySQL如何对排序的单行长度太大怎么办?
SET max_length_for_sort_data = 16;
max_length_for_sort_data,是MySQL中专门控制用于排序的行数据长度的一个参数,超过这个长度就要换另外一种算法,rowid排序。
相比于全字段排序流程图,rowid排序多访问了一次表t的主键索引,遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。
实际上就是从sort_buffer种依次取出id,然后到原表中查到city,name和age三个字段的结果,不需要再服务端耗费内存存储结果,是直接返回客户端的。
MySQL设计思想:如果内存足够,就要多利用内存,尽量减少磁盘的访问;
对于InnoDB表来说,rowid排序会要求回表多造成磁盘读,不会被优先选择。
并不是所有的order by语句都需要排序操作,MySQL之所以要生成临时表,并且在临时表上做排序操作,原因是因为数据都是无序的。
如果从city这个索引取出来的行,天然都是按照name递增排序的话,查询过程如下;
在市民表上创建city和name的联合索引,对应的SQL语句如下:
alter table t add index city_user(city,name);
查询过程:
采用覆盖索引能够对上述查询语句在进行优化 ,覆盖索引是指索引上的信息足够满足查询要求,不需要再回到主键索引上去取数据。
按照覆盖索引,创建city、name和age的联合索引,对应SQL语句如下:
alter table t add index city_user_age(city,name,age);
查询过程:
了解了执行orderby语句中全字段排序和rowid排序算法流程,在使用orderby语句的时候要清楚每个语句的排序逻辑的实现,分析出每个语句的执行对于系统资源的消耗。