本文翻譯自mysql 官網:order by optimization,mysql 版本:5.7。
這一部分描述了mysql何時會使用索引來滿足order by子句,filesort 操作會在索引不能生效的時候被用到,以及優化器對order by的執行計畫資訊。
order by後面有沒有跟著limit,可能會返回不同的記錄順序。
某些情況,mysql可能會使用索引來滿足order by子句並避免因執行 filesort 操作造成的額外的排序開銷。
即便order by子句並沒有完全與索引匹配,可能也會用到索引,只要索引所有未使用的部分和所有額外的order by 欄位在where子句中都是常量。如果索引沒有包含查詢的所有字段,那麼只有當索引訪問比其他訪問方法開銷更小的時候才會用到索引。
假設現在有乙個索引覆蓋了key_part1, key_part2這兩個字段,下面的查詢可能會用索引來解決order by 的部分。優化器是否真會這麼做取決於如果有乙個字段必須查詢而它又不在索引中的情況下,讀取索引是否比全表掃瞄更有效。
select * from t1
order by key_part1, key_part2;
但是,查詢使用的是select *,這可能會查詢出比索引列(key_part1 , key_part2)更多的字段。這種情況,掃瞄整個索引並在表中記錄裡查詢哪些不是索引列的開銷可能要比掃瞄整個表然後對結果集排序還要大。如果這樣,優化器通常就不會使用索引。如果select * 只查詢了索引列,那麼就會用到索引並避免排序操作。
如果表是乙個innodb 表,那麼表的主鍵預設也是索引的一部分,那麼下面的查詢就可以使用索引來解決order by:
select pk, key_part1, key_part2 from t1
order by key_part1, key_part2;
select * from t1
where key_part1 = constant
order by key_part2;
select * from t1
order by key_part1 desc, key_part2 desc;
select * from t1
where key_part1 = constant
order by key_part2 desc;
select * from t1
where key_part1 > constant
order by key_part1 asc;
select * from t1
where key_part1 < constant
order by key_part1 desc;
select * from t1
where key_part1 = constant1 and key_part2 > constant2
order by key_part2;
某些情況,mysql 不會使用索引來處理 order by,即便仍然會在 where 子句進行匹配操作時用到索引。例如下面這些:
select * from t1 order by key1, key2;
select * from t1 where key2=constant order by key1_part1, key1_part3;
select * from t1 where key2=constant order by key1_part1, key1_part3;
select * from t1 where key2=constant order by key1;
select * from t1 order by abs(key);
select * from t1 order by -key;
另外,用於排序的索引究竟可不可用還會受到字段別名的影響。假設 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 抑制了對結果的排序,分組操作之前不會有排序來決定結果。
注意:如果索引已經無法滿足 order by 子句,mysql 會執行 filesort 操作,它的意思是——讀取表中資料然後排序。filesort 會在查詢執行的時候有額外的排序時間。group by 預設隱式排序(即 group by 的字段沒有 asc 或 desc)。然而,依賴隱式 group by 排序或顯式 group by (即使用宣告的 asc 或 desc 來對 group by 的字段排序)排序都是不推薦的。要排序,請用 order by 子句。
為了獲取用於 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;
對於連 filesort 都無法生效的慢查詢,嘗試把max_length_for_sort_data系統變數調低到乙個適合觸發 filesort 的值。(乙個該值設定地太高的症狀是系統同時會出現高磁碟活動和低cpu 活動)
為了加快 order by 的速度,檢查一下你是否可以使用索引而不是額外的排序時間(博主:即使用 filesort)。如果無法做到,嘗試下面的優化策略:
使用 explain ,你可以檢查mysql 是否有用到索引來解決 order by 子句:
另外,如果 filesort 執行了,優化器跟蹤輸出包括乙個 filesort_summary 塊,例如:
"filesort_summary":
sort_mode 值提供了一些關於排序緩衝區中的元組的資訊。
explain 不會分辨優化器是否會執行記憶體中的 filesort 。記憶體 filesort 的使用可以在優化器的跟蹤報告中看到。
關於 order by 的優化部分,的確是非常複雜,其中比較重要的是關於 order by 如何利用索引的具體條件。在某些場合下,order by 是不會使用到索引的。
MySQL如何優化ORDER BY
某些情況中,mysql可以使用乙個索引來滿足order by子句,而不需要額外的排序。即使order by不確切匹配索引,只要where子句中的所有未使用的索引部分和所有額外的order by 列為常數,就可以使用索引。下面的查詢使用索引來解決order by部分 某些情況中,mysql可以使用乙個...
MySQL優化order by語句
優化order by語句 mysql的兩種排序方式 1.第一種就是通過有序索引順序掃瞄直接返回資料,常見的是使用explain分析查詢extra顯示using index,這種情況不需要額外的排序,高效率。2.通過返回資料進行排序,通常是使用explain分析查詢語句使用extra表示filesor...
MySQL查詢優化之ORDER BY
select id from uc user baseinfo where area code 020 limit 0,10 執行成功,當前返回 10 行,耗時 109ms.select id from uc user baseinfo where area code 020 limit 10,10...