1. 程式人生 > 其它 >轉載 SQL優化之order by 和 group by

轉載 SQL優化之order by 和 group by

轉載
https://blog.csdn.net/wuseyukui/article/details/72627667?utm_source=app&app_version=5.3.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

order by示例

示例資料

Case 1

Case 2

Case 3

Case 4

結論:order by子句,儘量使用Index方式排序,在索引列上遵循索引的最佳左字首原則。
複合(聯合)索引形如 key (‘A1’,’A2’,’A3’ ),排序的思路一般是,先按照A1來排序,A1相同,然後按照A2排序,以此類推,這樣對於(A1),(A1,A2), (A1,A2,A3)的索引都是有效的,但是對於(A2,A3)這樣的索引就無效了。儘量避免因索引欄位的缺失 或 索引欄位順序的不同 引起的FileSort排序。

order by 總結

FileSort排序演算法

演算法一:雙路排序演算法

只利用ORDERBY子句中包括的列物件進行排序(適用於有BLOB、TEXT型別的列物件參與的排序)

MySQL4.1之前的排序演算法,完整實現過程如下:

1) 按索引鍵或全表掃描的方式,讀取所有的元組,不匹配WHERE子句的元組被跳過;第一步需要從儲存讀入資料,引發I/O操作

2) 對於每一行,在緩衝區中儲存一對值(對值,包括排序關鍵字和元組指標)。緩衝區的大小是系統變數的sort_buffer_size設定的值。

3) 當緩衝區已滿,執行快排演算法(快速排序,qsort)對一個塊中的資料進行排序,將結果儲存在一個臨時檔案。儲存一個指向排序後的塊的指標(如果第二步所說的對值都能被緩衝區容納,則不會建立臨時檔案)。

4) 重複上述步驟,直到所有的行已經被讀取。

5) 執行一個多路歸併操作(操作物件是第三步生成的每一個有序的塊)彙集到“MERGEBUFF域”,然後存放到在第二個臨時檔案中。重複操作,直到第一個檔案的所有塊歸併後存入到第二個檔案;“MERGEBUFF域”是程式碼sql_sort.h中定義的巨集,值為7。

6) 重複以下操作(第7步和第8步),直到留下少於“MERGEBUFF2域”標明的塊數為止;“MERGEBUFF2域”是程式碼sql_sort.h中定義的巨集,值為15。

7) 在最後一次多路歸併操作中,把元組的指標(排序關鍵字的最後部分)寫入到一個結果檔案。

8) 在結果檔案中,按照排列的順序使用元組指標讀取元組(為了優化這項操作,MySQL讀入元組指標進入一個大的塊,對塊中元組指標進行排序而不是直接對資料排序,然後再用有序的元組指標獲取元組到元組快取,元組緩衝區的大小由read_rnd_buffer_size

引數控制)。第8步需要從儲存讀入資料,引發I/O操作

演算法二:單路排序演算法

除利用ORDERBY子句中包括的列物件外,還利用查詢目標列中的所有列物件進行排序(適用於除BLOB、TEXT型別外的所有的其他型別的排序)

MySQL4.1之後出現的改進演算法,減少一次I/O,需要增加緩衝區大小容納更多資訊。其具體實現過程如下:

1) 獲取與WHERE子句匹配的元組。這一步需要從儲存讀入資料,引發I/O操作。

2) 對於每一個元組,記錄排序鍵值、行的位置值、查詢所需的列。這一步記錄更多內容,需要更大快取,記憶體儲存一條元組的資訊的長度比演算法一的“對值”大許多,這可能引發排序速度問題(排序物件的長度變長,但是記憶體有限,所以就需把一次記憶體排序變為多次,進而影響排序的速度),為了控制這個問題,MySQL引入一個引數“max_length_for_sort_data”,如果這一步得到的元組長度大於這個值,則不使用演算法二。需要MySQL的使用者特別注意的是,在排序中,如果存在“很高磁碟I/O和很低的CPU利用率”的現象,則需要考慮調整“max_length_for_sort_data”的大小以變更換排序演算法。

3) 按照排序的鍵值,對元組(元組是第二步的結果)進行排序。

演算法二直接從緩衝區中的排序的元組中獲取有序的列資訊等(查詢的目的物件),而不是第二次訪問該表讀取所需的列。相比演算法一減少一次I/O。

FileSort優化策略

當無法使用索引列排序時,為了提高Order By的速度,應該嘗試一下優化:
1、避免使用 “select * ” 。查詢的欄位越多導致元組長度總合可能

超過max_length_for_sort_data的設定,導致無法使用單路排序演算法,只能用雙路排序演算法。
超過sort_buffer_size的設定,超出後會建立tmp檔案進行合併,導致多次IO

2、適當增大sort_buffer_size引數的設定

3、適當增大max_length_for_sort_data引數的設定

group by 示例

示例:

group by 總結

group by與order by的索引優化基本一樣,group by實質是先排序後分組,也就是分組之前必排序,遵照索引的最佳左字首原則可以大大提高group by的效率。

當無法使用索引列排序時,適當增大sort_buffer_size引數 + 適當增大max_length_for_sort_data引數可以提高filesort排序的效率。注意:可能會出現Using temporary,也就是說mysql在對查詢結果排序時使用了臨時表。

where高於having,能寫在where限定條件中的就儘量寫在where中。

**兩個相輔相成,所以放一起** **轉載2 https://blog.csdn.net/zzx125/article/details/79678770**

今天學習了下關於索引的最左字首的原理,小有成就感,在這裡做一個學習記錄,以後學習的時候可以直接找出來複習。

相信熟悉資料庫的大佬們跟索引達人們肯定都瞭解最索引的左字首原理,我在這裡還是再重複一下吧,文章還會結合實際例子來說明最左字首的原理。

實驗工具;mysql 5.5 + sqlyog


索引的最左字首原理:

通常我們在建立聯合索引的時候,也就是對多個欄位建立索引,相信建立過索引的同學們會發現,無論是oralce還是mysql都會讓我們選擇索引的順序,比如我們想在a,b,c三個欄位上建立一個聯合索引,我們可以選擇自己想要的優先順序,a、b、c,或者是b、a、c 或者是c、a、b等順序。為什麼資料庫會讓我們選擇欄位的順序呢?不都是三個欄位的聯合索引麼?這裡就引出了資料庫索引的最左字首原理。

比如:索引index1:(a,b,c)有三個欄位,我們在使用sql語句來查詢的時候,會發現很多情況下不按照我們想象的來走索引。

select * from table where c = '1'          這個sql語句是不會走index1索引的,select * from table where b =‘1’ and c ='2' 這個語句也不會走index1索引。

什麼語句會走index1索引呢?

答案是:

select * from table where a = '1'  

select * from table where a = '1' and b = ‘2’  

select * from table where a = '1' and b = ‘2’  and c='3'

我們可以發現一個共同點,就是所有走索引index1的sql語句的查詢條件裡面都帶有a欄位,那麼問題來了,index1的索引的最左邊的列欄位是a,是不是查詢條件中包含a就會走索引呢?

例如:select * from table where a = '1' and c= ‘2’這個sql語句,按照之前的理解,包含a欄位,會走索引,但是是不是所有欄位都走了索引呢?

我們來做個實驗:

我這裡有一個表:

  

建立了一個聯合索引,prinIdAndOrder裡面有三個欄位  PARENT_ID, MENU_ORDER, MENU_NAME

接下來測試之前的語句:

ELECT 
  t.* 
FROM
  sys_menu t 
WHERE t.`PARENT_ID` = '0' 
  AND t.`MENU_NAME` = '系統工具'

這一句sql就相當於之前的select * from table where a = '1' and c= ‘2’這個sql語句了,我們來看看解釋計劃:



可以看到走了索引prinIdAndOrder,但是旁邊的key_len=303,但道理key_len應該是大於303的,為什麼呢?因為PARENT_ID欄位的型別是varchar(100) NULL,所以key_len=100*3+2+1=303,但是還有MENU_NAME呢!具體的key_len的計算方法,大家可以百度,我的表的字符集是utf-8,不同字符集的表的計算方式不一樣。這裡的解釋計劃顯示key_len只有303,說明只是走了欄位PARENT_ID的索引,沒有走MENU_NAME的索引。

這也是最左字首原理的一部分,索引index1:(a,b,c),只會走a、a,b、a,b,c 三種類型的查詢,其實這裡說的有一點問題,a,c也走,但是隻走a欄位索引,不會走c欄位。

另外還有一個特殊情況說明下,select * from table where a = '1' and b > ‘2’  and c='3' 這種型別的也只會有a與b走索引,c不會走。

原因如下:

索引是有序的,index1索引在索引檔案中的排列是有序的,首先根據a來排序,然後才是根據b來排序,最後是根據c來排序,

像select * from table where a = '1' and b > ‘2’  and c='3' 這種型別的sql語句,在a、b走完索引後,c肯定是無序了,所以c就沒法走索引,資料庫會覺得還不如全表掃描c欄位來的快。不知道我說明白沒,感覺這一塊說的始終有點牽強。

文中如果出現有誤的地方麻煩大佬們指點。


</article>