MySQL Order By實現原理分析和Filesort優化

在MySQL中的ORDER BY有兩種排序實現方式:

1、利用有序索引獲取有序數據

2、文件排序

在使用explain分析查詢的時候,利用有序索引獲取有序數據顯示Using index。而文件排序顯示Using filesort。

1.利用有序索引獲取有序數據

          取出滿足過濾條件作為排序條件的字段,以及可以直接定位到行數據的行指針信息,在 Sort Buffer 中進行實際的排序操作,然后利用排好序的數據根據行指針信息返回表中取得客戶端請求的其他字段的數據,再返回給客戶端.

          這種方式,在使用explain分析查詢的時候,顯示Using index。而文件排序顯示Using filesort。

         注意:MySQL在查詢時最多只能使用一個索引。因此,如果WHERE條件已經占用了索引,那么在排序中就不使用索引了。

1.1 按照索引對結果進行排序:order by 使用索引是有條件

1)  返回選擇的字段,即只包括在有選擇的此列上(select后面的字段),不一定適應*的情況):

CREATE TABLE `test` (  
  `id` int(11) NOT NULLAUTO_INCREMENT,  
  `rdate` datetime NOT NULL,  
  `inventid` int(11) NOT NULL,  
  `customerid` int(11) NOT NULL,  
  `staffid` int(11) NOT NULL,  
  `data` varchar(20) NOT NULL,  
  PRIMARY KEY (`id`),  
  UNIQUE KEY `rdate`(`rdate`,`inventid`,`customerid`),  
  KEY `inventid` (`inventid`),  
  KEY `customerid` (`customerid`),  
  KEY `staffid` (`staffid`)  
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1

如:

mysql>   
explain select inventid from test where rdate='2011-12-1400:00:00' order by  inventid , customerid;  

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys |key    | key_len |ref      | rows |Extra                    |
+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+
|  1 |  SIMPLE      | test    |ref   |        rdate          |rdate  |      8     |const |   10   | Using where; Using index |
+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
Select選擇的列使用索引,而下面不使用索引:
mysql> explain select * from test where rdate='2011-12-14 00:00:00'order by  inventid , customerid ;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key     | key_len|ref     | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test   | ALL  | rdate               | NULL  | NULL    |NULL |  13   |Using where;Using filesort|
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+

2)  只有當ORDER BY中所有的列必須包含在相同的索引,并且索引的順序和order by子句中的順序完全一致,并且所有列的排序方向(升序或者降序)一樣才有,(混合使用ASC模式和DESC模式則不使用索引)

mysql>   
xplain select inventid from test order byrdate, inventid ;

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

| id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra       |

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

|  1 | SIMPLE      | test  | index | NULL          | rdate |16      | NULL |   13 |Using index|

+—-+————-+——-+——-+—————+——-+———+——+——+————-+

1 row in set (0.00 sec)

mysql>   
explain select inventid from test where rdate="2011-12-16" order by  inventid ,staffid;

+—-+————-+——-+——+—————+——-+———+——-+——+————————–

| id | select_type | table | type | possible_keys |key   | key_len | ref   | rows | Extra                       |

+—-+————-+——-+——+—————+——-+———+——-+——+————————–

|  1 | SIMPLE      | test | ref  | rdate         | rdate | 8       | const |    1 |Using where;Using filesort

+—-+————-+——-+——+—————+——-+———+——-+——+————————–

1 row in set (0.00 sec)

由于rdate, inventid使用了同一個索引。排序使用到了索引。這個也是滿足了前綴索引。但是order  by  inventid ,staffid;就不是使用了索引,因為staffid和inventid不是同一個索引

3)     where 語句與ORDER BY語句組合滿足最左前綴:

  1. mysql>   
    explain select inventid from test whererdate="2011-12-16" order by  inventid ;

+—-+————-+——-+——+—————+——-+———+——-+——+————————–+

| id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                    |

+—-+————-+——-+——+—————+——-+———+——-+——+————————–+

|  1 | SIMPLE      | test | ref  | rdate         | rdate | 8       | const |    1 | Using where;Using index |

+—-+————-+——-+——+—————+——-+———+——-+——+————————–+

1 row in set (0.00 sec)

4)     如果查詢聯接了多個表,只有在order by子句的所有列引用的是第一個表的列才可以。

5)     在其他的情況下,mysql使用文件排序  例如:

1)   where語句與order by語句,使用了不同的索引

2)   檢查的行數過多,且沒有使用覆蓋索引

3)   ORDER BY中的列不包含在相同的索引,也就是使用了不同的索引

4)   對索引列同時使用了ASC和DESC

5)   where語句或者ORDER BY語句中索引列使用了表達式,包括函數表達式

6)   where 語句與ORDER BY語句組合滿足最左前綴,但where語句中使用了條件查詢。查見第10句,雖然where與order by構成了索引最左有綴的條件,但是where子句中使用的是條件查詢。

mysql> explain select inventid from test where  rdate>"2011-12-16" order by  inventid;

+—-+————-+——-+——-+—————+——-+———+——+——+—————-

| id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra                                    

+—-+————-+——-+——-+—————+——-+———+——+——+—————-

|  1 |SIMPLE      | test  | range | rdate         | rdate | 8       | NULL |    1 | Using where; Using index;Usingfilesort |

+—-+————-+——-+——-+—————+——-+———+——+——+—————-

1 row in set (0.00sec)

               7)   當使用left join,使用右邊的表字段排序

2.文件排序

       這個 filesort 并不是說通過磁盤文件進行排序,而只是告訴我們進行了一個排序操作。即在MySQL Query Optimizer 所給出的執行計劃(通過 EXPLAIN 命令查看)中被稱為文件排序(filesort)

      文件排序是通過相應的排序算法,將取得的數據在內存中進行排序: MySQL需要將數據在內存中進行排序,所使用的內存區域也就是我們通過sort_buffer_size 系統變量所設置的排序區。這個排序區是每個Thread 獨享的,所以說可能在同一時刻在MySQL 中可能存在多個 sort buffer 內存區域。

在MySQL中filesort 的實現算法實際上是有兩種:

雙路排序:是首先根據相應的條件取出相應的排序字段和可以直接定位行數據的行指針信息,然后在sort buffer 中進行排序。

單路排序:是一次性取出滿足條件行的所有字段,然后在sort buffer中進行排序。

在MySQL4.1版本之前只有第一種排序算法雙路排序,第二種算法是從MySQL4.1開始的改進算法,主要目的是為了減少第一次算法中需要兩次訪問表數據的 IO 操作,將兩次變成了一次,但相應也會耗用更多的sortbuffer 空間。當然,MySQL4.1開始的以后所有版本同時也支持第一種算法,

MySQL主要通過比較我們所設定的系統參數 max_length_for_sort_data的大小和Query 語句所取出的字段類型大小總和來判定需要使用哪一種排序算法。如果 max_length_for_sort_data更大,則使用第二種優化后的算法,反之使用第一種算法。所以如果希望 ORDER BY 操作的效率盡可能的高,一定要主義max_length_for_sort_data 參數的設置。曾經就有同事的數據庫出現大量的排序等待,造成系統負載很高,而且響應時間變得很長,最后查出正是因為MySQL 使用了傳統的第一種排序算法而導致,在加大了max_length_for_sort_data 參數值之后,系統負載馬上得到了大的緩解,響應也快了很多。

2.1 MySQL 需要使用filesort 實現排序的實例

  假設有 Table A 和 B 兩個表結構分別如下:

# mysql
>show create table A\G
   *************************** 1. row ***************************
   Table: A
   Create Table: CREATE TABLE `A` (
   `id` int(11) NOT NULL default '0',
    `c2` char(2) default NULL,
   `c3` varchar(16) default NULL, 
   `c4` datetime default NULL, 
   PRIMARY KEY (`id`) 
   ) ENGINE=MyISAM DEFAULT CHARSET=utf8
#:mysql
> show create table B\G
   *************************** 1. row ***************************    Table: B
   Create Table: CREATE TABLE `B` ( 
   `id` int(11) NOT NULL default '0', 
   `c2` char(2) default NULL,
   `c3` varchar(16) default NULL, 
   PRIMARY KEY (`id`),
   KEY `B_c2_ind` (`c2`)
   ) ENGINE=MyISAM DEFAULT CHARSET=utf8

A.c2不是索引將使用: 

sky@localhost : example 01:54:23> EXPLAIN SELECT A.* FROM A,B WHERE A.id >2 AND A.c2 <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: 3
  Extra: Using where; Using filesort
 
*************************** 2. row ***************************
  id: 1
  select_type: SIMPLE
  table: B
  type: ref
  possible_keys: B_c2_ind
  key: B_c2_ind
  key_len: 7
  ref: example.A.c2
  rows: 2
  Extra: Using where; Using index

MySQL 從 Table A 中取出了符合條件的數據,由于取得的數據并不滿足 ORDER BY 條件,所以 MySQL 進行了 filesort 操作,其整個執行過程如下圖所示: 

1.gif

2.2 MySQL 需要使用Using temporary 臨時表來filesort

         如果order by的子句只引用了聯接中的第一個表,MySQL會先對第一個表進行排序,然后進行聯接。也就是expain中的Extra的Using Filesort.否則MySQL先把結果保存到臨時表(Temporary Table),然后再對臨時表的數據進行排序.此時expain中的Extra的顯示Using temporary Using Filesort.

例如如果我們的排序數據如果是兩個(或者更多個) Table 通過 Join所得出的,如下例所示:

sky@localhost : example 02:46:15> explain select A.* from A,B
where A.id > 2 and A.c2 < 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: 3
Extra: Using where; Using temporary; Using filesort
  *************************** 2. row ***************************
  id: 1
  select_type: SIMPLE
  table: B
  type: ref
  possible_keys: B_c2_ind
  key: B_c2_ind
  key_len: 7
  ref: example.A.c2
  rows: 2
  Extra: Using where

實際執行過程應該是如下圖所示:

2.gif 

 

3. 優化Filesort

當無法避免排序操作時,又該如何來優化呢?很顯然,應該盡可能讓 MySQL 選擇使用第二種單路算法來進行排序。這樣可以減少大量的隨機IO操作,很大幅度地提高排序工作的效率。

1. 加大 max_length_for_sort_data 參數的設置

在 MySQL 中,決定使用老式排序算法還是改進版排序算法是通過參數 max_length_for_ sort_data 來決定的。當所有返回字段的最大長度小于這個參數值時,MySQL 就會選擇改進后的排序算法,反之,則選擇老式的算法。所以,如果有充足的內存讓MySQL 存放須要返回的非排序字段,就可以加大這個參數的值來讓 MySQL 選擇使用改進版的排序算法。

2. 去掉不必要的返回字段

當內存不是很充裕時,不能簡單地通過強行加大上面的參數來強迫 MySQL 去使用改進版的排序算法,否則可能會造成 MySQL 不得不將數據分成很多段,然后進行排序,這樣可能會得不償失。此時就須要去掉不必要的返回字段,讓返回結果長度適應 max_length_for_sort_data 參數的限制。

3. 增大 sort_buffer_size 參數設置

增大 sort_buffer_size 并不是為了讓 MySQL選擇改進版的排序算法,而是為了讓MySQL盡量減少在排序過程中對須要排序的數據進行分段,因為分段會造成 MySQL 不得不使用臨時表來進行交換排序。

轉自:http://blog.csdn.net/hguisu/article/details/7161981

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/3114

(0)
s19930811s19930811
上一篇 2015-04-13
下一篇 2015-04-13

相關推薦

  • IoC/DIP其實是一種管理思想

    關于IoC的的概念提出來已經很多年了,其被用于一種面象對像的設計。我在這里再簡單的回顧一下這個概念。我先談技術,再說管理。 話說,我們有一個開關要控制一個燈的開和關這兩個動作,最常見也是最沒有技術含量的實現會是這個樣子: 然后,有一天,我們發現需要對燈泡擴展一下,于是我們做了個抽象類: 但是,如果有一天,我們發現這個開關可能還要控制別的不單單是燈泡的東西,我…

    Linux干貨 2016-08-15
  • Cobbler 自動化部署系統

    本人較懶,做好筆記,預排版,打印成 PDF 了。-_## Cobbler 自動化部署系統.pdf

    Linux干貨 2015-08-17
  • 運維的危險命令,用了必死(1)

    Linux命令行佷有用、很高效,也很有趣,但有時候也很危險,尤其是在你不確定你自己在正在做什么時候。這篇文章并不打算引來你對Linux或linux 命令行的憤怒。我們只是想讓你意識到在你運行某些命令時應該三思而后行。(譯注:當然,以下命令通常都是在root權限下才能將愚蠢發揮到無可救藥;在普通用戶身份下,破壞的只是自己的一畝三分地。)

    2017-11-16
  • 第二周作業

    1 linux 常見的文件管理命令都有哪些?其常用的使用方法。 ls 文件列出命令   常見選項  -l 顯示長文本信息          -d 顯示當前目錄信息          -a 顯示所有文件信息     &nb…

    Linux干貨 2016-09-26
  • 使用replicate-rewrite-db 實現復制映射 + Replicate_Wild_Do_Table實現復制過濾

    實驗環境:CentOS7.2 + MySQL5.7 node1(192.168.2.171)和node2(192.168.2.172) 為兩臺不同業務的MySQL服務器。 業務方有個需求,需要將node1上的employees庫的departments 、dept_manager 這2張表同步到 node2 的 hellodb 庫下面。 node1的empl…

    Linux干貨 2017-05-06
  • Linux基礎-用戶管理相關操作-week 4

    1.復制/etc/skel 目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其他用戶均沒有任何訪問權限  cp /etc/skel /home/tuser1 -rf chmod og=  /home/tuser1 -R 2.編輯/etc/group文件添加組hadoop echo hadoop:x:503 …

    Linux干貨 2016-11-21
欧美性久久久久