Mysql優化_ORDER BY和GROUP BY 的優化講解(單路排序和雙路排序)

OldBoy~發表於2018-01-20

ORDER BY 子句儘量使用Index方式排序,避免使用FileSort方式排序,儘可能在索引列上外城排序操作,遵照索引鍵的最佳左字首。如果不在索引列上,FileSort有兩種演算法,Mysql就要啟動雙路排序和單路排序。

什麼是雙路排序和單路排序?

雙路排序:Mysql4.1之前是使用雙路排序,字面的意思就是兩次掃描磁碟,最終得到資料,讀取行指標和ORDER BY列,對他們進行排序,然後掃描已經排好序的列表,按照列表中的值重新從列表中讀取對資料輸出。也就是從磁碟讀取排序欄位,在buffer進行排序,再從磁碟讀取其他欄位。檔案的磁碟IO非常耗時的,所以在Mysql4.1之後,出現了第二種演算法,就是單路排序。

單路排序:從磁碟讀取查詢所需要的所有列,按照ORDER BY在buffer對它進行排序,然後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取資料。並且把隨機IO變成了順序IO,但是它會使用更多的空間,因為它把每一行都儲存在了記憶體裡。

但是,問題來了,有可能單路排序演算法一次拿不出資料,那麼就還比雙路排序更消耗IO,效率更慢!


什麼情況下會導致單路排序失效呢?
在sort_buffer中,單路排序要比雙路排序佔很多空間,因為單路排序把所有的欄位都取出,所以有可能取出的資料的總大小超出了sort_buffer的容量,導致每次只能讀取sort_buffer容量大小的資料,進行排序(建立tmp檔案,多路合併),排完再取sort_buffer容量大小,再次排序...從而多次I/O。偷雞不成蝕把米。
比如:記憶體就是2M,一次查1000條資料剛好,也就是最大1000條資料,但是一次要查5000條,那麼不夠了,照這樣需要查5次剛好,如果把2M改為10M,那麼就剛好了

提高ORDER BY速度的技巧
1:ORDER BY時不要使用SELECT *,只查需要的欄位。
   a:當查詢的欄位大小綜合小於max_length_for_sort_data而且排序欄位不是TEXT|BLOB型別時,會用改進後的演算法---單路排序,否則用老演算法---多路排序。假設只需要查10個欄位,但是SELECT *會查80個欄位,那麼就容易把sort_buffer緩衝區用滿。
   b:兩種演算法的資料都有可能超出sort_buffer的容量,超出之後,會建立tmp檔案進行合併排序,導致多次I/O,但是用單路排序演算法的風險會更大一些,所以要提高sort_buffer_size大小。
2:增大sort_buffer_size引數大小
不管用哪種演算法,提高這個引數都會提高效率。當然要根據系統能力去提高,因為這個引數是針對每個程式的。
3:增大max_length_for_sort_data引數大小
提高這個引數,會增加用改進演算法的概率。但是如果設的太高,資料總量超出sort_buffer_size的概率就增大,明顯症狀是高的磁碟I/O活動和低的處理器使用率。

 

為排序使用索引
MYSQL兩種排序方式:檔案排序和掃描有序索引排序
Mysql能為排序和查詢使用相同的索引。也就是建立索引先把資料排序了,查詢的時候再利用索引,一舉兩得。

KEY a_b_c(a,b,c)
order by 能使用索引最左字首

ORDER BY a
ORDER BY a,b
ORDER BY a,b,c
ORDER BY a DESC,b DESC,c DESC

如果where使用縮印的最左字首定義為常量,則order by 能使用索引

WHERE a = const ORDER BY b,c
WHERE a = const AND b = const ORDER BY c
WHERE a = const AND b > const ORDER BY b,c

不適用索引進行排序

ORDER BY a ASC,b DESC, c DESC //排序不一致
WHERE g = const ORDER BY b,c //丟失a索引
WHERE a = const ORDER BY c //丟失b索引
WHERE a = const ORDER BY a,d //d不是索引的一部分
WHERE a in (....) ORDER BY b,c //對於排序來說,多個相等條件也是範圍查詢

GROUP BY的優化

GROUP BY實質上是先排序後進行分組,遵照索引的最佳左字首。
當無法使用索隱裂,考慮增大max_length_for_sort_data和sort_buffer_size的引數設定。
WHERE 高於 HAVING,能寫在WHERE限定的條件就不要去HAVING限定了。

 

 

相關文章