order by
在 Mysql 底层是如何执行的吗?
苏州
的全部人名字,而且按照姓名进行排序返回前 1000 我的的姓名、年龄,这条 sql 语句应该如何写?
CREATE TABLE user (
id int(11) NOT NULL,
city varchar(16) NOT NULL,
name varchar(16) NOT NULL,
age int(11) NOT NULL,
PRIMARY KEY (id),
KEY city (city)
) ENGINE=InnoDB;
复制代码
select city,name,age from user where city='苏州' order by name limit 1000;
复制代码
city
这个字段上添加了索引,固然城市的字段很小,不用考虑字符串的索引问题,以前有写过一篇关于如何给字符串的加索引的文章,有不了解朋友看一下这篇文章:
Mysql 性能优化:如何给字符串加索引?
Explain
来分析一下的这条查询语句的执行状况,结果以下图:
Extra
这个字段中的
Using filesort
表示的就是须要排序,MySQL 会给每一个线程分配一块内存用于排序,称为
sort_buffer
。
city
这棵索引树的结构,以下图:
city='苏州'
是从
ID3
到
IDX
这些记录。
city='苏州'
条件的
主键id
,也就是图中的
ID3
。
主键id索引
取出整行,取
name
、
city
、
age
三个字段的值,存入
sort_buffer
中。
city
取下一个记录的主键 id。
IDX
。
sort_buffer
中的数据按照字段
name
作快速排序。
全字段排序
,执行的流程图以下:
按name排序
这个动做,可能在内存中完成,也可能须要使用外部排序,这取决于排序所需的内存和参数
sort_buffer_size
。
sort_buffer_size
:就是 MySQL 为排序开辟的内存(sort_buffer)的大小。若是要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但若是排序数据量太大,内存放不下,则不得不利用
磁盘临时文件
辅助排序。
sort_buffer
和
临时文件
中执行的。
但这个算法有一个问题,就是若是查询要返回的字段不少的话,那么sort_buffer
里面要放的字段数太多,这样内存里可以同时放下的行数不多,要分红不少个临时文件,排序的性能会不好。
max_length_for_sort_data
这个参数使其使用另一种算法。max_length_for_sort_data,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,若是单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。
city
、
name
、
age
这三个字段的定义总长度是
36
,我把
max_length_for_sort_data
设置为 16,咱们再来看看计算过程有什么改变。设置的 sql 语句以下:
SET max_length_for_sort_data = 16;
复制代码
新的算法放入 sort_buffer 的字段,只有要排序的列(即 name 字段)和主键 id。算法
但这时,排序的结果就由于少了 city 和 age 字段的值,不能直接返回了,整个执行流程就变成以下所示的样子:sql
sort_buffer
,肯定放入两个字段,即
name
和
id
。
city='苏州'
条件的
主键id
,也就是图中的
ID3
。
主键id索引
取出整行,取 name、id 这两个字段,存入 sort_buffer 中。
city
取下一个记录的主键 id。
IDX
。
sort_buffer
中的数据按照字段
name
作快速排序。
这个执行流程的示意图以下,我把它称为rowid排序
。 性能优化
对比全字段排序
,rowid排序
多了一次回表查询
,便是多了第7步
的查询主键索引树。bash
order by
语句,都须要排序操做的。从上面分析的执行过程,咱们能够看到,MySQL 之因此须要生成临时表,而且在临时表上作排序操做,其缘由是
原来的数据都是无序的。
city
这个索引上取出来的行,自然就是按照 name 递增排序的话,是否是就能够不用再排序了呢?
(city,name)
联合索引,sql 语句以下:
alter table user add index city_user(city, name);
复制代码
city='苏州'
的记录,而且额外确保了,接下来按顺序取“下一条记录”的遍历过程当中,只要 city 的值是杭州,name 的值就必定是有序的。
Extra
字段中没有
Using filesort
了,也就是不须要排序了。并且因为
(city,name)
这个联合索引自己有序,
因此这个查询也不用把 4000 行全都读一遍,只要找到知足条件的前 1000 条记录就能够退出了。也就是说,在咱们这个例子里,只须要扫描 1000 次。
(city,name,age)
联合索引,这样在执行上面的查询语句就能使用覆盖索引了,避免了回表查询了,sql 语句以下:
alter table user add index city_user_age(city, name, age);
复制代码
order by
语句的几种算法流程。