MySQL查詢優化器--非SPJ優化--ORDERBY優化

MySQL提供了兩種排序算法操作(即ORDERBY操作,所以理解排序算法有助於理解ORDERBY優化),在查詢執行計劃中用“filesort”表示使用了外部文件進行排序。第一種算法是雙路排序算法,只利用ORDERBY子句中包括的列對象進行排序(適用於有BLOBTEXT類型的列對象參與的排序);第二種算法是單路排序算法,除利用ORDERBY子句中包括的列對象外,還利用查詢目標列中的所有列對象進行排序(適用於除BLOBTEXT類型外的所有的其他類型的排序)。

算法一:雙路排序算法的完整實現過程如下:

1)        按索引鍵或全表掃描的方式,讀取所有的元組,不匹配WHERE子句的元組被跳過;第一步需要從存儲讀入數據,引發I/O操作

2)        對於每一行,在緩衝區中存儲一對值(對值,包括排序關鍵字和元組指針)。緩衝區的大小是系統變量的sort_buffer_size設定的值。

3)        當緩衝區已滿,運行快排算法(快速排序,qsort)對一個塊中的數據進行排序,將結果存儲在一個臨時文件。保存一個指向排序後的塊的指針(如果第二步所說的對值都能被緩衝區容納,則不會創建臨時文件)。

4)        重複上述步驟,直到所有的行已經被讀取。

5)        執行一個多路歸併操作(操作對象是第三步生成的每一個有序的塊)彙集到“MERGEBUFF域”,然後存放到在第二個臨時文件中。重複操作,直到第一個文件的所有塊歸併後存入到第二個文件;“MERGEBUFF域”是代碼sql_sort.h中定義的宏,值爲7

6)        重複以下操作(第7步和第8步),直到留下少於“MERGEBUFF2域”標明的塊數爲止;“MERGEBUFF2域”是代碼sql_sort.h中定義的宏,值爲15

7)        在最後一次多路歸併操作中,把元組的指針(排序關鍵字的最後部分)寫入到一個結果文件。

8)        在結果文件中,按照排列的順序使用元組指針讀取元組(爲了優化這項操作,MySQL讀入元組指針進入一個大的塊,對塊中元組指針進行排序而不是直接對數據排序,然後再用有序的元組指針獲取元組到元組緩存,元組緩衝區的大小由read_rnd_buffer_size參數控制)。8步需要從存儲讀入數據,引發I/O操作

 

算法二:單路排序算法,改進算法一,減少一次I/O,需要增加緩衝區大小容納更多信息。其具體實現過程如下:

1)        獲取與WHERE子句匹配的元組。這一步需要從存儲讀入數據,引發I/O操作

2)        對於每一個元組,記錄排序鍵值、行的位置值、查詢所需的列。這一步記錄更多內容,需要更大緩存,內存存儲一條元組的信息的長度比算法一的“對值”大許多,這可能引發排序速度問題(排序對象的長度變長,但是內存有限,所以就需把一次內存排序變爲多次,進而影響排序的速度),爲了控制這個問題,MySQL引入一個參數“max_length_for_sort_data”,如果這一步得到的元組長度大於這個值,則不使用算法二。需要MySQL的使用者特別注意的是,在排序中,如果存在“很高磁盤I/O和很低的CPU利用率”的現象,則需要考慮調整“max_length_for_sort_data”的大小以變更換排序算法。

3)        按照排序的鍵值,對元組(元組是第二步的結果)進行排序。

算法二直接從緩衝區中的排序的元組中獲取有序的列信息等(查詢的目的對象),而不是第二次訪問該表讀取所需的列。相比算法一減少一次I/O

 

MySQL支持對於ORDERBY的優化,,下面我們通過具體示例說明。

首先讓我們做一些準備工作。創建表,命令如下:

CREATE TABLE t_o1 (a1 INT UNIQUE, b1 INT);

CREATE TABLE t_o2 (a2 INT UNIQUE, b2 INT);

 

示例1 在索引列上進行排序操作,MySQL支持利用索引進行排序優化。

在一個表的索引列上執行排序操作,查詢執行計劃如下:

mysql> EXPLAIN SELECT * FROM t_o1 ORDER BY a1;

+----+-------------+-------+------+------+----------------+

| id | select_type | table | type | key  | Extra          |

+----+-------------+-------+------+------+----------------+

|  1 | SIMPLE      | t_o1  | ALL  | NULL | Using filesort |

+----+-------------+-------+------+------+----------------+

1 row in set (0.00 sec)

從查詢執行計劃看,對錶進行了全表掃描,並進行了排序操作(Using filesort)。沒有利用索引對排序進行優化。

對索引列進行查詢,查詢執行計劃如下:

mysql> EXPLAIN SELECT a1 FROM t_o1 ORDER BY a1;

+----+-------------+-------+-------+------+-------------+

| id | select_type | table | type  | key  | Extra       |

+----+-------------+-------+-------+------+-------------+

|  1 | SIMPLE      | t_o1  | index | a1   | Using index |

+----+-------------+-------+-------+------+-------------+

1 row in set (0.00 sec)

從查詢執行計劃看,對錶進行了索引掃描,利用索引對排序進行優化。對比上一條SQL語句,不同之處在於目標列不同,本條SQL語句的目標列是索引列,不是表的全部字段。這說明MySQL支持利用索引消除排序的技術,但只限於目標列也是索引列。

 

示例2 排序下推,MySQL不支持。在非索引列上執行連接,然後排序,查詢執行計劃如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t_o1, t_o2 WHERE b1=b2  ORDER BY b1;

+----+-------------+-------+------+-------+----------------------------------------------------+

| id | select_type | table | type |  key  | Extra                                              |

+----+-------------+-------+------+-------+----------------------------------------------------+

|  1 | SIMPLE      | t_o1  | ALL  |  NULL | Using temporary; Using filesort                    |

|  1 | SIMPLE      | t_o2  | ALL  |  NULL | Using where; Using join buffer (Block Nested Loop) |

+----+-------------+-------+------+-------+----------------------------------------------------+

2 rows in set (0.00 sec)

從查詢執行計劃看,在表t_o1上分別進行了全表掃描後與t_o2表進行了連接,然後用臨時文件完成排序。這表明MySQL不可以把排序下推到基表單執行,然後再連接,不支持排序下推優化技術。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章