order by 排序

举例说明order by内部的执行流程:

表结构:

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;

查询语句:

select city,name,age from t where city='杭州' order by name limit 1000;

1.全字段排序

通过explain查看这个语句的执行情况:

上图中,extra字段中的"Using filesort"意思就是需要排序,mysql会给每个线程分配一块内存用于排序,称为 sort buffer. sort buffer是server层的。

为了说明这个sql语句的执行过程,先来看下city的索引示意图:

如上图,满足条件的为 ID(X)到ID(X+N)之间的这些记录。

通常情况下,该语句的执行流程如下:

a.初始化sort buffer,确定放入city,name,age三个字段

b.从索引city中找到第一个满足city=杭州的主键id,即图中的idx

c.到主键索引中取出idx的整行记录,取出name,city,age三个字段的值,放入sort buffer中。

d.从索引city中取下一个满足条件的主键id,重复b,c。直到city的值不是杭州为止。

e.对sort buffer中的数据按照字段name做快速排序。

f.根据排序结构取前1000行返回给客户端。

上述过程就是全字段排序。其中,按name排序这个操作,可能在内存中完成,也可能需要使用外部排序。这取决于排序所需内存及参数sort_buffer_size。sort_buffer_size就是sort_buffer的大小。如果要排序的数据量小于sort_buffer_size,排序就在内存中完成,否则,内存中放不下,就要利用磁盘临时文件辅助排序。

 

2. rowid排序

上述的全字段排序,只是将原表数据读了出来,之后的操作都是在sort_buffer和临时文件中执行的。但是如果查询需要返回的字段很多,内存中放不下,就需要用到多个临时文件(磁盘io很慢),性能会很差。

如果mysql认为排序的单行长度过大,可以通过修改参数 max_length_for_sort_data来使得mysql使用rowid排序。

max_length_for_sort_data,如果单行长度超过这个值,mysql就会放弃全字段排序,而使用rowid排序。

这种排序方法,只会将 要排序的列和主键放入sort buffer中,即只有name,id会放入sort buffer中。整个执行流程如下:

a.初始化sort buffer,确定放入name和id

b. 从索引中找到第一个满足条件的主键id,即IDX

c 到主键索引取出整行,取出name,id两个字段值,存入sort buffer

d, 从索引city找下一个满足条件的主键id,重复c,d ,直到不满足查询条件未知,即到IDY截止。

e,对sort buffer中的数据按照name值排序

f,遍历排序结果,取前1000行,并按照id的值到原表中取出city,name和age三个字段返回给客户端。

 

可以看出,rowid比全字段步骤多了一个 步骤:多访问了一次主键索引

上面已经说过,如果内存足够,会优先选择全字段排序,否则内存太小的话,mysql采用rowid排序算法,这样可以一次排更多行,但需要再回到原表(主键索引)去取数据。

mysql的设计思想:如果内存够,就优先多使用内存,尽量减少磁盘访问。

对于innoDB引擎的表来说,rowid排序会要求回表多,造成优先读,因为不会被优先选择。

3.order by的排序,mysql排序是成本比较高的操作。可以通过改变索引来优化这种操作。在这个例子中,可以给city和name建立联合索引,这样,只要city的值是杭州,那么取到 的name值就一定是有序的。而且,假如有5000行满足city="杭州",如果没有city和name联合索引,就需要把满足条件的五千行都扫描一遍,再按name排序,而有了city和name的联合索引之后,就不需要扫描那么多行了,只需要找到满足条件的前1000行就可以了,即扫描行数减少到了1000行。

就这个例子来说,现在不需要排序了,但还是最后要回表,因此还可以通过索引覆盖来避免回表:那就是建立city,name,age的联合索引,对于city字段值相同的行来说,还是按照name字段的值递增排序的,这样,仍然不需要排序,而且找到后需要返回的字段在联合索引上都有,也不需要再回表了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值