MySQL 优化 —— ORDER BY 优化
引言
本文翻譯自MySQL 官網:ORDER BY Optimization,MySQL 版本:5.7。
這一部分描述了MySQL何時會使用索引來滿足order by子句,filesort 操作會在索引不能生效的時候被用到,以及優化器對order by的執行計劃信息。
order by后面有沒有跟著limit,可能會返回不同的記錄順序。
一、使用索引來滿足 ORDER BY
某些情況,MySQL可能會使用索引來滿足order by子句并避免因執行 filesort 操作造成的額外的排序開銷。
即便order by子句并沒有完全與索引匹配,可能也會用到索引,只要索引所有未使用的部分和所有額外的order by 字段在where子句中都是常量。如果索引沒有包含查詢的所有字段,那么只有當索引訪問比其他訪問方法開銷更小的時候才會用到索引。
假設現在有一個索引覆蓋了key_part1, key_part2這兩個字段,下面的查詢可能會用索引來解決order by 的部分。優化器是否真會這么做取決于如果有一個字段必須查詢而它又不在索引中的情況下,讀取索引是否比全表掃描更有效。
- 在這個查詢中,兩個字段的復合索引可以使優化器避免排序。
但是,查詢使用的是select *,這可能會查詢出比索引列(key_part1 , key_part2)更多的字段。這種情況,掃描整個索引并在表中記錄里查找哪些不是索引列的開銷可能要比掃描整個表然后對結果集排序還要大。如果這樣,優化器通常就不會使用索引。如果select * 只查詢了索引列,那么就會用到索引并避免排序操作。
如果表是一個InnoDB 表,那么表的主鍵默認也是索引的一部分,那么下面的查詢就可以使用索引來解決order by:
SELECT pk, key_part1, key_part2 FROM t1ORDER BY key_part1, key_part2;- 在這個查詢中key_part1是常量,所有通過索引訪問到的記錄都會按照key_part2 來排序,并且如果where子句有足夠的選擇性使得索引范圍掃描比全表掃描開銷更小的話,那么覆蓋了(key_part1, key_part2)的復合索引就可以避免排序操作。
- 下面的兩個查詢,和之前沒有desc的同一查詢,索引的使用情況類似。
- 下面的兩個查詢,key_part1 和一個常量進行比較。如果 WHERE 子句有足夠的可選擇性來使索引區間掃描比全表掃描更優,那么索引就會被使用。
- 下面的查詢, ORDER BY 沒有使用 key_part1,但是所有被查詢的行都有一個常量的 key_part1 的值,因此依然會使用索引:
某些情況,MySQL 不會使用索引來處理 ORDER BY,即便仍然會在 WHERE 子句進行匹配操作時用到索引。例如下面這些:
- 查詢中的 ORDER BY 使用了不同的 索引:
- 查詢中的 ORDER BY 使用了不連續的索引部分:
- 查詢中的 ORDER BY 混用了 ASC 和 DESC:
- 用于查詢記錄的索引與 ORDER BY 中的索引不是同一個:
- 查詢中的 ORDER BY 包含了一個表達式,這個表達式包含索引列名之外的其他項:
- 查詢連接了多張表,并且 ORDER BY 中的字段并不都來自于第一個非常量數據表(博主:如果所有的 ORDER BY 和 GROUP BY子句中的字段都來自同一個表,那么這個表在連接時就作為第一張表。這里的意思是說,如果 ORDER BY 中的字段來自于不同的表,那么就不會用到索引)
- 查詢中包含不同的 ORDER BY 和 GROUP BY 表達式。
- ORDER BY 子句中的字段只有字段的前綴有索引。這種情況,索引就不會完全參與排序操作。例如,有一個列聲明為 CHAR(20),但是只有前10個字節建立了索引,那么索引就無法區分(博主:排序最本質的工作就是比較大小,這里的區分就是比較的意思)剩余的10個字節,這時就會使用 filesort 。
- 索引沒有以一定的順序存儲記錄。例如,memory 存儲引擎中用到的 HASH 索引就是這種不會對記錄進行排序的索引。
另外,用于排序的索引究竟可不可用還會受到字段別名的影響。假設 t1 表的 t1.a 字段建立了索引。下面的語句中,查詢列表中有這個字段 a 。它代表 t1.a ,因為 a 已經建立了索引,所以下面的語句就會用到索引:
SELECT a FROM t1 ORDER BY a;下面的語句中,查詢列表依然有 a 列,但此 a 非彼 a,這個 a 是一個別名。它代表的是 ABS(a),因為 索引是建立在 a 列上,所以 t1.a 上的索引不會生效:
SELECT ABS(a) AS a FROM t1 ORDER BY a;下面的語句中, ORDER BY 對 a 進行排序,而在查詢列表中,并沒有叫 a 的列。但是 t1 表中有一個列叫做 a,因此 ORDER BY 指向的是?t1.a ,t1.a 上的索引就會生效。(當然,排序的結果可能與 ORDER BY?ABS(a) 的排序完全不同)
SELECT ABS(a) AS b FROM t1 ORDER BY a;?默認地,MySQL 會對 GROUP BY col1, col2, ... 進行排序,就好像你的查詢中依然包含 ORDER BY col1, col2, ... 一樣。如果你顯式地包含一個 ORDER BY? 子句,包含了相同的字段列表,MySQL 會把它優化掉,排序依然會發生,且不會有性能犧牲。
如果一個查詢包含 GROUP BY 但是你想避免對結果排序的開銷。你可以通過指定 ORDER BY NULL 來抑制排序。例如:
INSERT INTO foo SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;優化器可能依然會用排序來實現分組操作。ORDER BY NULL 抑制了對結果的排序,分組操作之前不會有排序來決定結果。
注意:
GROUP BY 默認隱式排序(即 GROUP BY 的字段沒有 ASC 或 DESC)。然而,依賴隱式 GROUP BY 排序或顯式 GROUP BY (即使用聲明的 ASC 或 DESC 來對 GROUP BY 的字段排序)排序都是不推薦的。要排序,請用 ORDER BY 子句。
二、 使用 filesort 來滿足 ORDER BY
如果索引已經無法滿足 ORDER BY 子句,MySQL 會執行 filesort 操作,它的意思是——讀取表中數據然后排序。filesort 會在查詢執行的時候有額外的排序時間。
為了獲取用于 filesort 操作的內存,優化器會預先分配一個固定大小為?sort_buffer_size 個字節。每一個session 會話可以通過改變這個值來避免過度的內存消耗,或者在必要時分配更多內存。
如果結果集真的大到內存已經無法裝下,那么 filesort 操作會在這種必要的時候使用臨時的磁盤文件。有些類型的查詢尤其適合完成內存內的 filesort 操作。例如,下面的查詢形式,優化器就可以使用 filesort 在內存中,而不是使用臨時文件來高效地處理 ORDER BY 操作:
SELECT ... FROM single_table ... ORDER BY non_index_column [DESC] LIMIT [M,]N;下面的查詢在 web 應用中非常常見,它們用于讓一個很大的結果集展示很少的一些記錄。例如:
SELECT col1, ... FROM t1 ... ORDER BY name LIMIT 10; SELECT col1, ... FROM t1 ... ORDER BY RAND() LIMIT 15;三、影響 ORDER BY 優化
對于連 filesort 都無法生效的慢查詢,嘗試把 max_length_for_sort_data 系統變量調低到一個適合觸發 filesort 的值。(一個該值設置地太高的癥狀是系統同時會出現高磁盤活動和低CPU 活動)
為了加快 ORDER BY 的速度,檢查一下你是否可以使用索引而不是額外的排序時間(博主:即使用 filesort)。如果無法做到,嘗試下面的優化策略:
- 調大 sort_buffer_size 變量的值。理想狀態下,這個值應該大到足夠整個結果集可以容納在排序緩沖區(為了避免磁盤寫入和合并次數),但是該值最小也必須要足夠容納15個元組。(合并最多15個臨時磁盤文件,每個文件必須有至少一個元組的內存空間)。
- 考慮到存儲在排序緩沖區中的列值的大小受到 max_sort_length 系統變量的影響。例如,如果元組存儲長字符串字段的值,并且你調大了 max_sort_length?的值,那么排序緩沖區元組的大小也會變大,并且你可能同時需要增大 sort_buffer_size 的大小。對于作為字符串表達式結果計算的列值(例如那些需要一個字符串參數的函數),filesort 算法無法得知表達式的最大長度,所以就一定會給每個元組分配 max_sort_length 的字節數。如果要監控合并次數(合并臨時文件),可以檢查 Sort_merge_passes 狀態變量。
- 調大 read_rnd_buffer_size 變量可以在同一時間讀取更多的記錄。
- 改變 tmpdir 系統變量的值,使其指向一個具有大量空閑空間的專用文件系統。該變量值會列出幾個以循環方式使用的路徑。使用這個功能可以讓負載分散到多個目錄。在Unix 系統上以冒號(:)分隔路徑,在 Windows 系統上以分號(;)分隔路徑。路徑應該命名位于不同物理磁盤上的文件系統中的目錄,而不是同一磁盤上的不同分區。
四、ORDER BY 的執行計劃信息
使用 EXPLAIN ,你可以檢查MySQL 是否有用到索引來解決 ORDER BY 子句:
- 如果在輸出的執行計劃中 Extra 列不包含 "Using filesort" ,那么索引排序生效,filesort 不會執行。
- 如果在輸出的執行計劃中 Extra 列包含 "Using filesort",那么排序就沒有用到索引,filesort 就會執行。
另外,如果 filesort 執行了,優化器跟蹤輸出包括一個 filesort_summary 塊,例如:
"filesort_summary": {"rows": 100,"examined_rows": 100,"number_of_tmp_files": 0,"sort_buffer_size": 25192,"sort_mode": "<sort_key, packed_additional_fields>" }sort_mode 值提供了一些關于排序緩沖區中的元組的信息。
- <sort_key, rowid> :它表示排序緩沖區元組是包含原表行的排序鍵值和行ID的對。元組被排序鍵排序,記錄 ID 被用于從表中讀取數據。
- <sort_key, additional_fields>:它表示排序緩沖區元組包含排序鍵值和查詢涉及到的字段。元組被排序鍵排序,字段值會直接從元組中讀取。
- <sort_key, packed_additional_fields>:與前面的變體類似,但是附加的列是緊密地打包在一起的,而不是使用固定長度的編碼。
EXPLAIN 不會分辨優化器是否會執行內存中的 filesort 。內存 filesort 的使用可以在優化器的跟蹤報告中看到。
總結
關于 ORDER BY 的優化部分,的確是非常復雜,其中比較重要的是關于 ORDER BY 如何利用索引的具體條件。在某些場合下,ORDER BY 是不會使用到索引的。
總結
以上是生活随笔為你收集整理的MySQL 优化 —— ORDER BY 优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux查询.gz日志,linux2-
- 下一篇: python 神经网络原理_神经网络理论