MySQL 查詢優化(六): MySQL 的查詢優化排序優化機制

對結果進行排序操作的代價可能很高,因此可以通過避免排序或讓參與排序的數據行更少來優化查詢性能。

當 MySQL 不能使用索引產生有序結果時,它必須對數據行進行排序。這有可能是在內存中進行也可能是在磁盤進行,但 MySQL 始終將這個過程稱之爲 filesort,即便實際上並沒有使用一個文件。

如果用於排序的值可以一次性放入排序緩存中,MySQL 可以在內存中使用快排算法進行排序。如果 MySQL 不能在內存中進行排序,則會在磁盤中按塊逐塊排序。它對每個塊使用快排算法,然後在將這些排序好的塊合併到結果中。

有兩個文件排序(filesort)算法:

  • 兩次遍歷(Two passes,舊的算法):讀取ORDER BY 指定列對應的數據行指針,然後對其進行排序,再遍歷已排序的列表並重新讀取輸出需要的數據行。由於從數據表讀取了兩次數據行,因此兩次遍歷算法的代價可能非常高。而且第二次讀取會導致很多的隨機 I/O 訪問,這對 MyISAM 引擎(由於 MyISAM 依靠操作系統緩存保存數據,他使用系統調用獲取每一行)的性能影響尤其大。另一方面,在排序過程中存儲最少的數據量,因此如果要排序的數據行都在內存中,則對於存儲更少數據和二次讀取最終結果的數據行的情況來說,代價就會更低。
  • 單次遍歷:讀取需要查詢的所有列,然後依據 ORDER BY指定的列進行排序,再遍歷排序好的列表並輸出指定的數據列。這個算法在 MySQL 4.1及更高的版本才支持。由於它避免了數據錶行的二次讀取以及將更多的隨機 I/O 轉換爲順序 I/O,因此這種方式性能更高,尤其是對於 I/O 跨度大的數據集查詢而言。然而,這樣意味着它可能佔據更多的內存空間,這是因爲它保存了每個數據行需要讀取的列,而不僅僅是需要排序的列。這意味着對於排序緩衝區來說,利用率更低,而且文件排序需要處理更多的排序合併。

很難說哪種算法更有效,對每個算法來說都會有最優和最壞案例。MySQL 在數據表全部列加上用於排序的列的大小不超過 max_length_for_sort_data 時會使用單次遍歷算法。可以通過修改這個參數影響排序算法的選擇。

需要注意的是,MySQL 的 filesort使用的臨時存儲空間可能會超出你的預期,這是因爲它對每個排序元素都分配了固定大小的存儲空間。這些存儲空間要足夠大以便容下存儲最大的元素,而且 VARCHAR這類字段使用的是對應的最大長度。而且,如果使用的是 UTF-8字符集,MuSQL 會對每個字符分配3個字節。結果是,我們會發現那些沒怎麼優化的查詢會導致磁盤上的臨時存儲空間是數據表自身存儲空間的好幾倍。

而在對聯合查詢進行排序時,MySQL 可能會在查詢執行過程中執行兩次文件排序。如果 ORDER BY 子句只是引用聯合查詢的第一張表,MySQL 可以先對這個表進行文件排序,然後再處理聯合查詢。如果是這種情況,在 EXPLAIN 時會在 Extra 字段顯示“Using filesort”。而對於其他的排序情況——例如排序不是針對第一張表,或者是 ORDER BY 使用的列對應了不止一個數據表,MySQL 必須使用臨時表緩存查詢結果,然而在聯合查詢完成後,再對臨時表進行文件排序。在這種情況下,EXPLAIN 會在 Extra 字段顯示“Using temorary; Using filesort”。如果包含 LIMIT 約束的話,會發生在文件排序後,因此臨時表和文件排序的存儲空間可能非常大。

MySQL 5.6在只需要對數據行的子集(例如 LIMIT)進行排序時,引入了一個重大改進。相對於對整個結果集進行排序再返回部分數據,MySQL 有時候會在排序的時候直接丟棄掉不需要的數據行來提高效率。不管怎麼樣,排序也需要小心使用,很可能會導致存儲佔用的飆升最終導致系統負荷過大。

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