(转)MySQL ORDER BY 的实现分析

原文:http://blog.chinaunix.net/space.php?uid=10449864&do=blog&id=2956835

在http://www.mysqlperformanceblog.com/2009/03/05/what-does-using-filesort-mean-in-mysql/这个文章里面,了解到了filesort的意思,而刚好淘宝的DBA--张朝阳也做了这个实验,下面这个对说明就来自张朝阳的blog,说的很经典!
    我就按照文章做个实验,感受下实际效果。
    总的来说,在 MySQL 中的ORDER BY有两种排序实现方式,一种是利用有序索引获取有序数据,另一种则是通过相应的排序算法,将取得的数据在内存中进行排序。
创建2个表:


CREATE TABLE `A` (  
`c1` int(11) NOT NULL DEFAULT '0',  
`c2` char(2) DEFAULT NULL,  
`c3` varchar(16) DEFAULT NULL,  
`c4` datetime DEFAULT NULL,  
PRIMARY KEY (`c1`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;


 CREATE TABLE `B` (  
`c1` int(11) NOT NULL DEFAULT '0',  
`c2` char(2) DEFAULT NULL,  
`c3` varchar(16) DEFAULT NULL,  
PRIMARY KEY (`c1`),  
KEY `b_c2` (`c2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


 自己往数据库里面添加写数据,ok!


 1. 利用有序索引进行排序,实际上就是当我们 Query 的 ORDER BY 条件和 Query 的执行计划中所利用的 Index 的索引键(或前面几个索引键)完全一致,且索引访问方式为 rang、ref 或者 index 的候,MySQL 可以利用索引顺序而直接取得已经排好序的数据。这种方式的 ORDER BY 基本上可以说是最优的排序方式了,因为 MySQL 不需要进行实际的排序操作。
这种情况下,mysql相关查询语句如下:
mysql> SELECT A.* FROM A,B WHERE A.c1> 2 AND A.c1< 5 AND A.c2 = B.c2 ORDER BY A.c1;
+----+------+-------------+---------------------+
| c1 | c2   | c3          | c4                  |
+----+------+-------------+---------------------+
|  3 | c    | c is three  | 2009-03-10 09:30:15 |
|  4 | d    | d is four   | 2009-03-10 09:30:28 |
+----+------+-------------+---------------------+
2 rows in set (0.00 sec)


mysql> explain SELECT A.* FROM A,B WHERE A.c1> 2 AND A.c1< 5 AND A.c2 = B.c2 ORDER BY A.c1\G;
*************************** 1. row ***************************
           id: 1   select_type: SIMPLE
        table: A
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 1
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: B
         type: ref
possible_keys: b_c2
          key: b_c2
      key_len: 7
          ref: test.A.c2
         rows: 2
        Extra: Using where; Using index
2 rows in set (0.00 sec)
内部执行结构图如下:


2、 通过相应的排序算法,将取得的数据在内存中进行排序方式,MySQL 比需要将数据在内存中进行排序,所使用的内存区域也就是我们通过 sort_buffer_size 系统变量所设置的排序区。这个排序区是每个 Thread 独享的,所以说可能在同一时刻在 MySQL 中可能存在多个 sort buffer 内存区域。
第二种方式在 MySQL Query Optimizer 所给出的执行计划(通过 EXPLAIN 命令查看)中被称为 filesort。在这种方式中,主要是由于没有可以利用的有序索引取得有序的数据,MySQL只能通过将取得的数据在内存中进行排序然后再将数据返回给客户端。在 MySQL 中 filesort 的实现算法实际上是有两种的,一种是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行指针信息,然后在 sort buffer 中进行排序。另外一种是一次性取出满足条件行的所有字段,然后在 sort buffer 中进行排序。
在 MySQL4.1 版本之前只有第一种排序算法,第二种算法是从 MySQL4.1开始的改进算法,主要目的是为了减少第一次算法中需要两次访问表数据的 IO 操作,将两次变成了一次,但相应也会耗用更多的 sort buffer 空间。当然,MySQL4.1开始的以后所有版本同时也支持第一种算法,MySQL 主要通过比较我们所设定的系统参数 max_length_for_sort_data 的大小和 Query 语句所取出的字段类型大小总和来判定需要使用哪一种排序算法。如果 max_length_for_sort_data 更大,则使用第二种优化后的算法,反之使用第一种算法。所以如果希望 ORDER BY 操作的效率尽可能的高,一定要主义 max_length_for_sort_data 参数的设置。


 这种情况下,mysql相关查询语句如下:


mysql> SELECT A.* FROM A,B WHERE A.c1 > 2 AND A.c1 < 5 AND A.c2 = B.c2 ORDER BY A.c2 ;
+----+------+-------------+---------------------+
| c1 | c2   | c3          | c4                  |
+----+------+-------------+---------------------+
|  3 | c    | c is three  | 2009-03-10 09:30:15 |
|  4 | d    | d is four   | 2009-03-10 09:30:28 |
+----+------+-------------+---------------------+
2 rows in set (0.00 sec)


mysql> explain SELECT A.* FROM A,B WHERE A.c1 > 2 AND A.c1 < 5 AND A.c2 = B.c2 ORDER BY A.c2\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: A
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 1
        Extra: Using where; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: B
         type: ref
possible_keys: b_c2
          key: b_c2
      key_len: 7
          ref: test.A.c2
         rows: 2
        Extra: Using where; Using index
2 rows in set (0.00 sec)
内部执行结构图如下:


在 MySQL 中,filesort 操作还有一个比较奇怪的限制,那就是其数据源必须是来源于一个 Table,所以,如果我们的排序数据如果是两个(或者更多个) Table 通过 Join所得出的,那么 MySQL 必须通过先创建一个临时表(Temporary Table),然后再将此临时表的数据进行排序.


这种情况下,mysql相关查询语句如下:


mysql> SELECT A . *  FROM A, B WHERE A.c1 >2 AND A.c1 <5 AND A.c2 = B.c2 ORDER BY B.c3;
+----+------+-------------+---------------------+
| c1 | c2   | c3          | c4                  |
+----+------+-------------+---------------------+
|  3 | c    | c is three  | 2009-03-10 09:30:15 |
|  4 | d    | d is four   | 2009-03-10 09:30:28 |
+----+------+-------------+---------------------+
2 rows in set (0.00 sec)


mysql> explain SELECT A . *  FROM A, B WHERE A.c1 >2 AND A.c1 <5 AND A.c2 = B.c2 ORDER BY B.c3\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: A
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 1
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: B
         type: ref
possible_keys: b_c2
          key: b_c2
      key_len: 7
          ref: test.A.c2
         rows: 2
        Extra: Using where
2 rows in set (0.00 sec)

内部执行结构图如下:

 

posted @ 2011-10-28 16:07  wwh  阅读(220)  评论(0编辑  收藏  举报