version : 5.7, from 8.2.1.14 ORDER BY Optimizationhtml
本節描述MySQL什麼時候可使用索引來知足ORDER BY子句,當不能使用索引時使用filesort,以及優化器中有關ORDER BY的執行計劃信息。mysql
一個order by語句對於有沒有使用limit可能存在執行差別。詳細內容查看8.2.1.17 LIMIT Query Optimization。算法
在某些狀況下,MySQL可能會使用索引來知足一個ORDER BY子句,並避免執行filesort 操做時涉及的額外排序。sql
雖然ORDER BY並不徹底精確地匹配索引,可是索引仍是會被使用,只要在WHERE子句中,全部未被使用的那部分索引(一個索引多個字段-聯合索引的狀況)以及全部ORDER BY字段都是一個常量就沒問題,都會走到索引而不是filesort。bash
這裏咱們有一張表tx_order,session
CREATE TABLE `tx_order` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT ,
`serial_number` varchar(255) NOT NULL ,
`order_status` int unsigned DEFAULT 0 NOT NULL ,
`market_id` varchar(10) DEFAULT NULL ,
`market_name` varchar(255) DEFAULT NULL ,
`shop_id` varchar(50) DEFAULT NULL ,
`shop_name` varchar(100) DEFAULT NULL ,
`mobile` varchar(64) DEFAULT NULL ,
`create_date` datetime DEFAULT NULL ,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2333702 DEFAULT CHARSET=utf8;
複製代碼
而且添加索引函數
alter table tx_order add index idx_market_date(market_id,create_date);
複製代碼
在接下來的sql中分析order by對索引的使用狀況。其中MySQL優化器實際執行sql是否使用索引仍是表掃描取決於二者的效率。性能
desc select market_id,create_date from tx_order.tx_order order by market_id,create_date;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index
複製代碼
然而這句sql中的查詢字段都在索引中,若是查詢字段不被包含在索引中,如「select market_id,create_date,market_name」。這種狀況下,掃描整個索引而且查找錶行以查不在索引中的列,這樣的操做的代價可能比表掃描更高,此時優化器可能不會使用索引。優化
desc select market_id,create_date,market_name from tx_order.tx_order order by market_id,create_date;
1 SIMPLE tx_order ALL 1671956 100 Using filesort
複製代碼
在InnoDB中,咱們知道主鍵(彙集索引)自己是索引的一部分,下面這個查詢中索引就會被使用。ui
desc select id,market_id,create_date from tx_order.tx_order order by market_id,create_date;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index
複製代碼
desc select market_id,create_date from tx_order.tx_order where market_id = '1009' order by create_date;
1 SIMPLE tx_order ref idx_market_date idx_market_date 33 const 170398 100 Using where; Using index
複製代碼
desc select market_id,create_date from tx_order.tx_order order by market_id desc ,create_date desc ;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index
desc select market_id,create_date from tx_order.tx_order order by market_id asc ,create_date desc ;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index; Using filesort
複製代碼
desc select market_id,create_date from tx_order.tx_order where market_id > '1009' order by market_id asc;
1 SIMPLE tx_order range idx_market_date idx_market_date 33 835978 100 Using where; Using index
desc select market_id,create_date from tx_order.tx_order where market_id < '1009' order by market_id desc;
1 SIMPLE tx_order range idx_market_date idx_market_date 33 230966 100 Using where; Using index
複製代碼
desc select market_id,create_date from tx_order.tx_order where market_id = '1009' and create_date>'2018-01-01' order by create_date desc;
1 SIMPLE tx_order range idx_market_date idx_market_date 39 94002 100 Using where; Using index
複製代碼
在一些狀況下,雖然MySQL對where條件處理的時候用會用到索引,可是不可以用索引來解析order by, 看下面的例子。
desc select market_id,create_date from tx_order.tx_order where market_id='1009' order by market_id ,create_date ;
1 SIMPLE tx_order ref idx_market_id,idx_market_type_create_date idx_market_id 33 const 138084 100 Using where; Using index; Using filesort
複製代碼
desc select market_id,create_date from tx_order.tx_order order by market_id asc ,create_date desc;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index; Using filesort
複製代碼
desc select mobile from tx_order.tx_order order by abs(mobile);
1 SIMPLE tx_order index idx_mobile 768 1671956 100 Using index; Using filesort
複製代碼
desc select a.market_id from tx_order.tx_order a ,tx_order_item b where a.id = b.order_id and a.market_id = '1009' order by a.market_id,b.sku;
1 SIMPLE b ALL idx_order_create 1 100 Using filesort
1 SIMPLE a eq_ref PRIMARY,idx_market_date PRIMARY 8 tx_order.b.order_id 1 10.19 Using where
複製代碼
desc select market_id,create_date from tx_order.tx_order group by market_id,create_date order by create_date;
1 SIMPLE tx_order index idx_market_date idx_market_date 39 1671956 100 Using index; Using temporary; Using filesort
複製代碼
desc select mobile from tx_order.tx_order order by mobile desc ;
1 SIMPLE tx_order ALL 1671956 100 Using filesort
複製代碼
有些狀況,使用的表索引的類型不能按順序保存行。例如,對於HEAP表的HASH索引狀況即如此。
排序索引的可用性可能受列別名的使用影響。
在下面的語句中,排序受到影響,不會使用索引.
desc select abs(market_id) as aa from tx_order.tx_order order by aa;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index; Using filesort
複製代碼
可是,下面的語句中,雖然查詢字段有使用別名,可是真實的排序字段仍是索引中的字段,那麼排序仍是使用索引的。
desc select abs(market_id) as aa from tx_order.tx_order order by market_id;
1 SIMPLE tx_order index idx_market_date 39 1671956 100 Using index
複製代碼
在默認狀況下,對於"group by col2,col2,..."這樣的語句,MySQL會同時會包含"order by col2,col2,..."等同於你顯示的加速"order by col2,col2,..."排序,這種狀況下優化器的處理是沒有性能損失的。
對於這個默認狀況,若是你想避開默認排序,可使用 order by null 來避免,例如:
desc select market_id,count(market_id) from tx_order.tx_order group by market_id order by null ;
複製代碼
優化器可能仍然選擇使用排序來實現分組操做。ORDER BY NULL 禁止對結果進行排序,而不是經過分組操做進行先前排序以肯定結果。
注意
GROUP BY默認狀況下隱式排序(即,在沒有列ASC或 列的DESC指示符的狀況下GROUP BY)。可是,不推薦依賴於隱式 GROUP BY排序(即,在沒有ASC或 DESC指示符的狀況下排序)或顯式排序GROUP BY(即,經過 對列使用顯式ASC或DESC指示符GROUP BY)。要生成給定的排序順序,有必要供一個 ORDER BY子句。
當沒法使用索引排序的時候,MySQL使用filesort掃描表給結果集排序,相應的filesort在整個查詢過程當中產生了額外的排序階段。
爲了支持filesort,優化器實現會分配必定數量的內存sort_buffer_size區域,這塊內存區域是每一個session獨佔的,而且能夠更改這個變量值。
若是filesort數據集太大,在內存中沒法實現排序,優化器會使用一塊磁盤做爲臨時文件來作排序。某些查詢特別適合內存排序完成filesort的操做,例如優化器能夠有效的利用內存排序,而不須要臨時文件實現。例如
desc select * from tx_order.tx_order order by market_name desc limit 10;
1 SIMPLE tx_order ALL 1671956 100 Using filesort
複製代碼
Using temporary的例子
desc select market_name from tx_order.tx_order order by RAND() desc limit 10;
1 SIMPLE tx_order ALL 1671956 100 Using temporary; Using filesort
複製代碼
對於filesort的慢查詢,能夠嘗試修改 max_length_for_sort_data 標量來達到效果,控制filesort選擇算法的觸發點,能夠嘗試調低 max_length_for_sort_data 值。(若是增大了max_length_for_sort_data的值,而且磁盤使用率上升,cpu使用率降低,)詳細資料請閱讀 Mysql 排序優化與索引使用(轉)。
要提升ORDER BY速度,請檢查是否可讓MySQL使用索引而不是額外的排序階段。若是沒法作到這一點,請嘗試如下策略:
增長 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上用分號字符()分隔路徑。路徑應命名位於不一樣物理磁盤上的文件系統中的目錄 ,而不是同一磁盤上的不一樣分區。
使用 EXPLAIN (參見8.8.1 Optimizing Queries with EXPLAIN),能夠檢查MySQL是否可使用索引來解析ORDER BY子句.
另外,filesort執行的時候優化器的trace能夠輸出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>"
}
複製代碼
對於MySQL的trace,詳細請參考Chapter 8 Tracing the Optimizer.
想要寫出高效可靠的排序查詢,你須要搞明白order by大概的執行過程,這裏能夠參考How MySQL executes ORDER BY,Mysql 排序優化與索引使用(轉)這兩篇文章。
咱們在寫sql語句而且使用order by的時候,首先考慮知足索引條件,若是不知足那麼知足內存中filesort,最壞的狀況就是臨時文件出現了,固然這種狀況是咱們最不想看到的。
同時這裏要說一下個人我的經驗:
開放過程當中多去琢磨sql,多看執行計劃,有效的避免慢查詢,提升服務的性能。