1. 程式人生 > 其它 >排序、分組SQL深度優化

排序、分組SQL深度優化

技術標籤:資料庫mysql

文章目錄


一、Order by與Group by優化

示例一

 EXPLAIN select * from employees where name ='LiLei' and position ='dev' ORDER BY age

在這裡插入圖片描述
分析:
利用最左字首法則:中間欄位不能斷,因此查詢用到了name索引,從key_len=74也能看出,age索引列用 在排序過程中,因為Extra欄位裡沒有using filesort

關於什麼是using filesort不清楚的小夥伴可以看一下 Explain分析SQl語句詳解

示例二

 EXPLAIN select * from employees where name ='LiLei' ORDER BY position

在這裡插入圖片描述
分析:
從explain的執行結果來看:key_len=74,查詢使用了name索引,由於用了position進行排序,跳過了 age,出現了Using filesort

示例三

 EXPLAIN select * from employees where name ='LiLei' ORDER BY age,position

在這裡插入圖片描述
分析:
查詢只用到索引name,age和position用於排序,無Using filesort。

示例四

 EXPLAIN select * from employees where name ='LiLei' ORDER BY position,age

在這裡插入圖片描述
分析:
和示例三 中explain的執行結果一樣,但是出現了Using filesort,因為索引的建立順序為 name,age,position,但是排序的時候age和position 顛倒位置 了。

示例五

在這裡插入圖片描述
分析:

雖然排序的欄位列與索引順序一樣,且order by預設升序,這裡position desc變成了降序,導致與索引的排序方式不同,從而產生Using filesort。Mysql8以上版本有降序索引可以支援該種查詢方式。

優化總結:

1、MySQL支援兩種方式的排序filesort和index,Using index是指MySQL掃描索引本身完成排序。index 效率高,filesort效率低。

2、order by滿足兩種情況會使用Using index。 1) order by語句使用索引最左前列。 2) 使用where子句與order by子句條件列組合滿足索引最左前列。

3、儘量在索引列上完成排序,遵循索引建立(索引建立的順序)時的最左字首法則。

4、如果order by的條件不在索引列上,就會產生Using filesort。

5、能用覆蓋索引儘量用覆蓋索引

6、group by與order by很類似,其實質是先排序後分組,遵照索引建立順序的最左字首法則。對於group by的優化如果不需要排序的可以加上order by null禁止排序。注意,where高於having,能寫在where中 的限定條件就不要去having限定了。

二、Using filesort檔案排序原理詳解

filesort檔案排序方式

  1. 單路排序:是一次性取出滿足條件行的所有欄位,然後在sort buffer中進行排序;用trace工具可 以看到sort_mode資訊裡顯示< sort_key, additional_fields >或者< sort_key, packed_additional_fields

  2. 雙路排序(又叫回表排序模式):是首先根據相應的條件取出相應的排序欄位和可以直接定位行 資料的行 ID,然後在 sort buffer 中進行排序,排序完後需要再次取回其它需要的欄位;用trace工具 可以看到sort_mode資訊裡顯示< sort_key, rowid >

MySQL 通過比較系統變數 max_length_for_sort_data(預設1024位元組) 的大小和需要查詢的欄位總大小來 判斷使用哪種排序模式。

  1. 如果 max_length_for_sort_data 比查詢欄位的總長度大,那麼使用 單路排序模式;

  2. 如果 max_length_for_sort_data 比查詢欄位的總長度小,那麼使用 雙路排序模式。

單路排序的詳細過程:

  1. 從索引name找到第一個滿足 name = ‘a’ 條件的主鍵 id
  2. 根據主鍵 id 取出整行,取出所有欄位的值,存入 sort_buffer 中
  3. 從索引name找到下一個滿足 name = ‘a’ 條件的主鍵 id
  4. 重複步驟 2、3 直到不滿足 name = ‘a’
  5. 對 sort_buffer 中的資料按照欄位 position 進行排序
  6. 返回結果給客戶端

雙路排序的詳細過程:

  1. 從索引 name 找到第一個滿足 name = ‘a’ 的主鍵id
  2. 根據主鍵 id 取出整行,把排序欄位 position 和主鍵 id 這兩個欄位放到 sort buffer 中
  3. 從索引 name 取下一個滿足 name = ‘a’ 記錄的主鍵 id
  4. 重複 3、4 直到不滿足 name = ‘a’
  5. 對 sort_buffer 中的欄位 position 和主鍵 id 按照欄位 position 進行排序
  6. 遍歷排序好的 id 和欄位 position,按照 id 的值回到原表中取出 所有欄位的值返回給客戶端

其實對比兩個排序模式,單路排序會把所有需要查詢的欄位都放到 sort buffer 中,而雙路排序只會把主鍵 和需要排序的欄位放到 sort buffer 中進行排序,然後再通過主鍵回到原表查詢需要的欄位。 如果 MySQL 排序記憶體配置的比較小並且沒有條件繼續增加了,可以適當把 max_length_for_sort_data 配 置小點,讓優化器選擇使用雙路排序演算法,可以在sort_buffer 中一次排序更多的行,只是需要再根據主鍵 回到原表取資料。

如果 MySQL 排序記憶體有條件可以配置比較大,可以適當增大 max_length_for_sort_data 的值,讓優化器 優先選擇全欄位排序(單路排序),把需要的欄位放到 sort_buffer 中,這樣排序後就會直接從記憶體裡返回查 詢結果了。

所以,MySQL通過 max_length_for_sort_data 這個引數來控制排序,在不同場景使用不同的排序模式, 從而提升排序效率。

注意: 如果全部使用sort_buffer記憶體排序一般情況下效率會高於磁碟檔案排序,但不能因為這個就隨便增 大sort_buffer(預設1M),mysql很多引數設定都是做過優化的,不要輕易調整