高效使用order by排序

357 阅读3分钟

前提:针对排序字段,建立索引,需要对数据库数据存储B+树模型有一定的了解

一、排序的方式

想要针对某个字段进行排序,那么这个字段必须建立索引。这样可以避免MySQL进行全表扫描。MySQL中,有一个叫做sort buffer的内存,这块内存是用于排序的。本质上是先将符合条件的数据放入这块内存中,然后进行排序。比如以下SQL语句:person表中主键为id

SELECT name,age,sex From person WHERE age = 18 order by name;

首先,会将满足条件的数据读入sort buffer,针对读入的字段,区分为了以下2种排序模式:

  • “全字段”排序
  • “特定字段”排序

1.“全字段”排序

简而言之,将所有需要的字段读入内存中,此时是name,age,sex字段。然后根据age字段进行排序,将排序结果返回。那么为什么需要第二种排序方式呢?

2.“特定字段”排序

“全字段”排序有个很明显的缺点,那就是内存的占用率。当一个表很大时,读入的数据会很多,但sort buffer是有大小限制的,为了辅助排序,因此会使用临时文件。当使用临时文件是会涉及I/O操作的,因此这儿可能会有大量的性能消耗。为了减少甚至是避免临时文件的使用,只读入排序字段和主键字段,针对上述例子便是age,id字段。根据age字段进行排序后,再根据id获取数据。这种方式就能减少临时文件的使用,但需要增加一次“回表”操作。

二、如何优化排序

上述二种方式各有优劣,一个会增加磁盘I/O,一个会多一次回表操作。具体采用哪一方式本质上是一种策略问题,MySQL会尽可能的使用内存在进行排序。max_length_for_sort_data,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。下面的优化针对于“全字段”排序。

1.建立联合索引

“全字段”排序需要使用临时文件,而使用临时文件的目的是进行排序。那么就存在一种可能,读入到sort buffer的数据已经是排序好的,不再需要进行排序。这个时候就需要建立联合索引(age,name)。此时的联合索引B+树上本质是根据age和name进行排序后建立的。根据索引的最左前缀原则,可以很轻易的获取到已经排序好的name数据。比如关键字的顺序:(18,'a'),(18,'b'),(19,'a'),(19,'b')。获取age=18的数据时,name数据('a','b')是排序好的。

2.字段覆盖

“全字段”排序中有一个步骤是读入所有的字段,但二级索引B+树中,大概率不会保存所有的数据,比如age索引B+树,它存放的数据是(age,id)的结果集,此时需要用id获取完整数据,这儿是一次回表操作。为了避免这次回表操作,使用字段覆盖,直接让二级索引B+树中存放的数据能覆盖所有的查询字段,此时便不会通过id获取数据。这儿也是建立联合索引,可以是二级索引为联合索引,也可以是主键索引为联合索引。比如二级索引(age,name,sex),此时通过age查找出来的数据,name是已经排序好的,并且可以sex的值也是可以直接获取到的。