mysql group by底層原理


 

  參考地址:https://www.cnblogs.com/bonelee/p/6359250.html

      參考地址:https://blog.csdn.net/xtdhqdhq/article/details/18408905

  由於GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之后的分組操作。當然,如果在分組的時候還使用了其他的一些聚合函數,那么還需要一些聚合函數的計算。所以,在GROUP BY 的實現過程中,與 ORDER BY 一樣也可以利用到索引。

    在Mysql 中,GROUP BY 的實現同樣有多種(三種)方式,其中有兩種方式會利用現有的索引信息來完成 GROUP BY另外一種為完全無法使用索引的場景下使用。下面我們分別針對這三種實現方式做一個分析。

 1、使用松散(Loose)索引掃描實現 GROUP BY

  何謂松散索引掃描實現 GROUP BY 呢?實際上就是當 MySQL 完全利用索引掃描來實現 GROUP BY 的時候,並不需要掃描所有滿足條件的索引鍵即可完成操作得出結果。

  意思是索引中用於group的字段,沒必要包含多列索引的全部字段。例如:有一個索引idx(c1,c2,c3),那么group by c1、group by c1,c2這樣c1或c1、c2都只是索引idx的一部分。要注意的是,索引中用於group的字段必須符合索引的“最左前綴”原則。group by c1,c3是 不會使用松散的索引掃描的。

  下面我們通過一個示例來描述松散索引掃描實現 GROUP BY,在示例之前我們需要首先調整一下 group_message 表的索引,將 gmt_create 字段添加到 group_id 和 user_id 字段的索引中:

  然后再看如下 Query 的執行計划:

sky@localhost: example 09:26:15> EXPLAIN
 -> SELECT user_id,max(gmt_create)
 -> FROM group_message
 -> WHERE group_id < 10
 -> GROUP BY group_id,user_id\G --符合最左前綴索引,會執行索引
 *************************** 1. row ***************************
 id: 1
 select_type: SIMPLE
 table: group_message
 type: range
 possible_keys: idx_gid_uid_gc
 key: idx_gid_uid_gc
 key_len: 8
 ref: NULL
 rows: 4
 Extra: Using where; Using index for group-by

  我們看到在執行計划的 Extra 信息中有信息顯示“Using index for group-by”,實際上這就是告訴我們,MySQL Query Optimizer 通過使用松散索引掃描來實現了我們所需要的 GROUP BY 操作。

  下面這張圖片描繪了掃描過程的大概實現:

  要利用到松散索引掃描實現 GROUP BY,需要至少滿足以下幾個條件:

  ◆GROUP BY 條件字段需要符合最左前綴索引;
  ◆在使用GROUP BY 的同時,只能使用 MAX 和 MIN 這兩個聚合函數;
  ◆如果引用到了該索引中 GROUP BY 條件之外的字段條件的時候,必須以常量形式存在;

  例如:EXPLAIN SELECT max(date),1 FROM before_ten_broker GROUP BY asset_id;

      

  ◆如果查詢中有where條件,則條件必須為索引,不能包含非索引的字段

  

  

 

  為什么松散索引掃描的效率會很高?

  因為在沒有WHERE子句,也就是必須經過全索引掃描的時候, 松散索引掃描需要讀取的鍵值數量與分組的組數量一樣多,也就是說比實際存在的鍵值數目要少很多。而在WHERE子句包含范圍判斷式或者等值表達式的時候, 松散索引掃描查找滿足范圍條件的每個組的第1個關鍵字,並且再次讀取盡可能最少數量的關鍵字。

2.使用緊湊(Tight)索引掃描實現 GROUP BY

  緊湊索引掃描實現 GROUP BY 和松散索引掃描的區別:

    主要在於他需要在掃描索引的時候,讀取所有滿足條件的索引鍵,然后再根據讀取的數據來完成 GROUP BY 操作,得到相應結果。

sky@localhost : example 08:55:14> EXPLAIN
-> SELECT max(gmt_create)
-> FROM group_message
-> WHERE group_id = 2 --常量
-> GROUP BY user_id\G --組合索引的第二個字段,會執行索引
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: group_message
type: ref
possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc
key: idx_gid_uid_gc
key_len: 4
ref: const
rows: 4
Extra: Using where; Using index
row in set (0.01 sec)

  這時候的執行計划的 Extra 信息中已經沒有“Using index for group-by”了,但並不是說 MySQL 的 GROUP BY 操作並不是通過索引完成的,只不過是需要訪問 WHERE 條件所限定的所有索引鍵信息之后才能得出結果。這就是通過緊湊索引掃描來實現 GROUP BY 的執行計划輸出信息。

  下面這張圖片展示了大概的整個執行過程:

  在 MySQL 中,MySQL Query Optimizer 首先會選擇嘗試通過松散索引掃描來實現 GROUP BY 操作,當發現某些情況無法滿足松散索引掃描實現 GROUP BY 的要求之后,才會嘗試通過緊湊索引掃描來實現。

  當 GROUP BY 條件字段不是連續或者索引前綴部分的時候,MySQL Query Optimizer 無法使用松散索引掃描,設置無法直接通過索引完成 GROUP BY 操作,因為缺失的索引鍵信息無法得到。但是,如果 Query 語句中存在一個常量值來引用缺失的索引鍵,則可以使用緊湊索引掃描完成 GROUP BY 操作因為常量填充了搜索關鍵字中的“差距”,可以形成完整的索引前綴。這些索引前綴可以用於索引查找。而如果需要排序GROUP BY結果,並且能夠形成索引前綴的搜索關鍵字,MySQL還可以避免額外的排序操作,因為使用有順序的索引的前綴進行搜索已經按順序檢索到了所有關鍵字。

3.使用臨時表實現 GROUP BY

  MySQL 在進行 GROUP BY 操作的時候要想利用所有,必須滿足 GROUP BY 的字段必須同時存放於同一個索引中,且該索引是一個有序索引(如 Hash 索引就不能滿足要求)。而且,並不只是如此,是否能夠利用索引來實現 GROUP BY 還與使用的聚合函數也有關系。

  前面兩種 GROUP BY 的實現方式都是在有可以利用的索引的時候使用的,當 MySQL Query Optimizer 無法找到合適的索引可以利用的時候,就不得不先讀取需要的數據,然后通過臨時表來完成 GROUP BY 操作。

  

sky@localhost : example 09:02:40> EXPLAIN
-> SELECT max(gmt_create)
-> FROM group_message
-> WHERE group_id > 1 and group_id < 10 -- 不是常量
-> GROUP BY user_id\G -- 就算是組合索引第二個字段,但是第一個不是常量,那也不會執行索引
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: group_message
type: range
possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc
key: idx_gid_uid_gc
key_len: 4
ref: NULL
rows: 32
Extra: Using where; Using index; Using temporary; Using filesort

  這次的執行計划非常明顯的告訴我們 MySQL 通過索引找到了我們需要的數據,然后創建了臨時表,又進行了排序操作,才得到我們需要的 GROUP BY 結果。整個執行過程大概如下圖所展示:

  當 MySQL Query Optimizer 發現僅僅通過索引掃描並不能直接得到 GROUP BY 的結果之后,他就不得不選擇通過使用臨時表然后再排序的方式來實現 GROUP BY了。

  在這樣示例中即是這樣的情況。 group_id 並不是一個常量條件,而是一個范圍,而且 GROUP BY 字段為 user_id。所以 MySQL 無法根據索引的順序來幫助 GROUP BY 的實現,只能先通過索引范圍掃描得到需要的數據,然后將數據存入臨時表,然后再進行排序和分組操作來完成 GROUP BY。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM