亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

MySQL優化GROUP BY(松散索引掃描與緊湊索引掃描)

2024-07-24 13:09:40
字體:
來源:轉載
供稿:網友

滿足GROUP BY子句的最一般的方法是掃描整個表并創建一個新的臨時表,表中每個組的所有行應為連續的,然后使用該臨時表來找到組并應用累積函數(如果有)。在某些情況中,MySQL能夠做得更好,即通過索引訪問而不用創建臨時表。
       為GROUP BY使用索引的最重要的前提條件是所有GROUP BY列引用同一索引的屬性,并且索引按順序保存其關鍵字。是否用索引訪問來代替臨時表的使用還取決于在查詢中使用了哪部分索引、為該部分指定的條件,以及選擇的累積函數。
       由于GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之后的分組操作。當然,如果在分組的時候還使用了其他的一些聚合函數,那么還需要一些聚合函數的計算。所以,在GROUP BY 的實現過程中,與 ORDER BY 一樣也可以利用到索引。在MySQL 中,GROUP BY 的實現同樣有多種(三種)方式,其中有兩種方式會利用現有的索引信息來完成 GROUP BY,另外一種為完全無法使用索引的場景下使用。下面我們分別針對這三種實現方式做一個分析。

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

對“松散索引掃描”的定義,本人看了很多網上的介紹,都不甚明白。在此邏列如下:
定義1:松散索引掃描,實際上就是當 MySQL 完全利用索引掃描來實現 GROUP BY 的時候,并不需要掃描所有滿足條件的索引鍵即可完成操作得出結果。
定義2:優化Group By最有效的辦法是當可以直接使用索引來完全獲取需要group的字段。使用這個訪問方法時,MySQL使用對關鍵字排序的索引的類型(比如BTREE索引)。這使得索引中用于group的字段不必完全涵蓋WHERE條件中索引對應的key。由于只包含索引中關鍵字的一部分,因此稱為松散的索引掃描。
意思是索引中用于group的字段,沒必要包含多列索引的全部字段。例如:有一個索引idx(c1,c2,c3),那么group by c1、group by c1,c2這樣c1或c1、c2都只是索引idx的一部分。要注意的是,索引中用于group的字段必須符合索引的“最左前綴”原則。group by c1,c3是不會使用松散的索引掃描的
例如:
explain
SELECT group_id,gmt_create
FROM group_message
WHERE user_id>1
GROUP BY group_id,gmt_create;
本人理解“定義2”的例子說明
有一個索引idx(c1,c2,c3)
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
索引中用于group的字段為c1,c2
不必完全涵蓋WHERE條件中索引對應的key(where條件中索引,即為c1;c1對應的key,即為idx)
索引中用于group的字段(c1,c2)只包含索引中關鍵字(c1,c2,c3)的一部分,因此稱為松散的索引掃描。
要利用到松散索引掃描實現GROUP BY,需要至少滿足以下幾個條件:
◆ 查詢針對一個單表
◆ GROUP BY 條件字段必須在同一個索引中最前面的連續位置;
GROUP BY包括索引的第1個連續部分(如果對于GROUP BY,查詢有一個DISTINCT子句,則所有DISTINCT的屬性指向索引開頭)。
◆ 在使用GROUP BY 的同時,如果有聚合函數,只能使用 MAX 和 MIN 這兩個聚合函數,并且它們均指向相同的列。
◆ 如果引用(where條件中)到了該索引中GROUP BY 條件之外的字段條件的時候,必須以常量形式存在,但MIN()或MAX() 函數的參數例外;
   或者說:索引的任何其它部分(除了那些來自查詢中引用的GROUP BY)必須為常數(也就是說,必須按常量數量來引用它們),但MIN()或MAX() 函數的參數例外。
補充:如果sql中有where語句,且select中引用了該索引中GROUP BY 條件之外的字段條件的時候,where中這些字段要以常量形式存在。
◆ 如果查詢中有where條件,則條件必須為索引,不能包含非索引的字段

松散索引掃描
explain
SELECT group_id,user_id
FROM group_message
WHERE group_id between 1 and 4
GROUP BY group_id,user_id;
松散索引掃描
explain
SELECT group_id,user_id
FROM group_message
WHERE user_id>1 and group_id=1
GROUP BY group_id,user_id;
非松散索引掃描
explain
SELECT group_id,user_id
FROM group_message
WHERE abc=1
GROUP BY group_id,user_id;
非松散索引掃描
explain
SELECT group_id,user_id
FROM group_message
WHERE user_id>1 and abc=1
GROUP BY group_id,user_id;
松散索引掃描,此類查詢的EXPLAIN輸出顯示Extra列的Using index for group-by

下面的查詢提供該類的幾個例子,假定表t1(c1,c2,c3,c4)有一個索引idx(c1,c2,c3):

SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;
SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;

由于上述原因,不能用該快速選擇方法執行下面的查詢:

1、除了MIN()或MAX(),還有其它累積函數,例如:
     SELECT c1, SUM(c2) FROM t1 GROUP BY c1;
2、GROUP BY子句中的域不引用索引開頭,如下所示:
     SELECT c1,c2 FROM t1 GROUP BY c2, c3;
3、查詢引用了GROUP BY部分后面的關鍵字的一部分,并且沒有等于常量的等式,例如:
     SELECT c1,c3 FROM t1 GROUP BY c1, c2;
這個例子中,引用到了c3(c3必須為組合索引中的一個),因為group by 中沒有c3。并且沒有等于常量的等式。所以不能使用松散索引掃描
可以這樣改一下:SELECT c1,c3 FROM t1 where c3='a' GROUP BY c1, c2
下面這個例子不能使用松散索引掃描
SELECT c1,c3 FROM t1 where c3='a' GROUP BY c1, c2
為什么松散索引掃描的效率會很高?
答:因為在沒有WHERE 子句,也就是必須經過全索引掃描的時候, 松散索引掃描需要讀取的鍵值數量與分組的組數量一樣多,也就是說比實際存在的鍵值數目要少很多。而在WHERE 子句包含范圍判斷式或者等值表達式的時候, 松散索引掃描查找滿足范圍條件的每個組的第1 個關鍵字,并且再次讀取盡可能最少數量的關鍵字。

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

緊湊索引掃描實現 GROUP BY 和松散索引掃描的區別主要在于:
緊湊索引掃描需要在掃描索引的時候,讀取所有滿足條件的索引鍵,然后再根據讀取出的數據來完成 GROUP BY 操作得到相應結果。
這時候的執行計劃的 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 無法使用松散索引掃描。
這時檢查where 中的條件字段是否有索引的前綴部分,如果有此前綴部分,且該部分是一個常量,且與group by 后的字段組合起來成為一個連續的索引。這時按緊湊索引掃描。

SELECT max(gmt_create)
FROM group_message
WHERE group_id = 2
GROUP BY user_id

需讀取group_id=2的所有數據,然后在讀取的數據中完成group by操作得到結果。(這里group by 字段并不是一個連續索引,正好where 中group_id正好彌補缺失的索引鍵,又恰好是一個常量,因此使用緊湊索引掃描)
group_id user_id 這個順序是可以使用該索引。如果連接的順序不符合索引的“最左前綴”原則,則不使用緊湊索引掃描。

以下例子使用緊湊索引掃描

GROUP BY中有一個差距,但已經由條件user_id = 1覆蓋。
explain
SELECT group_id,gmt_create
FROM group_message
WHERE user_id = 1 GROUP BY group_id,gmt_create

GROUP BY不以關鍵字的第1個元素開始,但是有一個條件提供該元素的常量
explain
SELECT group_id,gmt_create
FROM group_message
WHERE group_id = 1 GROUP BY user_id,gmt_create

下面的例子都不使用緊湊索引掃描
user_id,gmt_create 連接起來并不符合索引“最左前綴”原則
explain
SELECT group_id,gmt_create
FROM group_message
WHERE user_id = 1 GROUP BY gmt_create
group_id,gmt_create 連接起來并不符合索引“最左前綴”原則
explain
SELECT gmt_create
FROM group_message
WHERE group_id=1 GROUP BY gmt_create;

 3、使用臨時表實現 GROUP BY

MySQL Query Optimizer 發現僅僅通過索引掃描并不能直接得到 GROUP BY 的結果之后,他就不得不選擇通過使用臨時表然后再排序的方式來實現 GROUP BY了。在這樣示例中即是這樣的情況。 group_id 并不是一個常量條件,而是一個范圍,而且 GROUP BY 字段為 user_id。所以 MySQL 無法根據索引的順序來幫助 GROUP BY 的實現,只能先通過索引范圍掃描得到需要的數據,然后將數據存入臨時表,然后再進行排序和分組操作來完成 GROUP BY。
explain
SELECT group_id
FROM group_message
WHERE group_id between 1 and 4
GROUP BY user_id;
示例數據庫文件

-- ---------------------------------------------------------- Host:             127.0.0.1-- Server version:        5.1.57-community - MySQL Community Server (GPL)-- Server OS:          Win32-- HeidiSQL version:       7.0.0.4156-- Date/time:          2012-08-20 16:52:10-- --------------------------------------------------------/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;/*!40101 SET NAMES utf8 */;/*!40014 SET FOREIGN_KEY_CHECKS=0 */;-- Dumping structure for table test.group_messageDROP TABLE IF EXISTS `group_message`;CREATE TABLE IF NOT EXISTS `group_message` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_id` int(10) unsigned DEFAULT NULL, `user_id` int(10) unsigned DEFAULT NULL, `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `abc` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `group_id_user_id_gmt_create` (`group_id`,`user_id`,`gmt_create`)) ENGINE=MyISAM AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;-- Dumping data for table test.group_message: 0 rowsDELETE FROM `group_message`;/*!40000 ALTER TABLE `group_message` DISABLE KEYS */;INSERT INTO `group_message` (`id`, `group_id`, `user_id`, `gmt_create`, `abc`) VALUES	(1, 1, 1, '2012-08-20 09:25:35', 1),	(2, 2, 1, '2012-08-20 09:25:39', 1),	(3, 2, 2, '2012-08-20 09:25:47', 1),	(4, 3, 1, '2012-08-20 09:25:50', 2),	(5, 3, 2, '2012-08-20 09:25:52', 2),	(6, 3, 3, '2012-08-20 09:25:54', 0),	(7, 4, 1, '2012-08-20 09:25:57', 0),	(8, 4, 2, '2012-08-20 09:26:00', 0),	(9, 4, 3, '2012-08-20 09:26:02', 0),	(10, 4, 4, '2012-08-20 09:26:06', 0),	(11, 5, 1, '2012-08-20 09:26:09', 0),	(12, 5, 2, '2012-08-20 09:26:12', 0),	(13, 5, 3, '2012-08-20 09:26:13', 0),	(14, 5, 4, '2012-08-20 09:26:15', 0),	(15, 5, 5, '2012-08-20 09:26:17', 0),	(16, 6, 1, '2012-08-20 09:26:20', 0),	(17, 7, 1, '2012-08-20 09:26:23', 0),	(18, 7, 2, '2012-08-20 09:26:28', 0),	(19, 8, 1, '2012-08-20 09:26:32', 0),	(20, 8, 2, '2012-08-20 09:26:35', 0),	(21, 9, 1, '2012-08-20 09:26:37', 0),	(22, 9, 2, '2012-08-20 09:26:40', 0),	(23, 10, 1, '2012-08-20 09:26:42', 0),	(24, 10, 2, '2012-08-20 09:26:44', 0),	(25, 10, 3, '2012-08-20 09:26:51', 0),	(26, 11, 1, '2012-08-20 09:26:54', 0);/*!40000 ALTER TABLE `group_message` ENABLE KEYS */;/*!40014 SET FOREIGN_KEY_CHECKS=1 */;/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;


注:相關教程知識閱讀請移步到MYSQL教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产日韩中文字幕在线| 亚洲一区二区自拍| 日韩电影中文 亚洲精品乱码| 国产亚洲美女久久| 国产美女精品视频免费观看| 日韩欧美国产免费播放| 国产精品视频免费观看www| 一区国产精品视频| 国产亚洲美女精品久久久| 欧美电影《睫毛膏》| 亚洲国产精品系列| 久久青草精品视频免费观看| 亚洲最新视频在线| 欧美怡春院一区二区三区| 亚洲人成电影在线观看天堂色| 一区二区欧美在线| 欧美精品第一页在线播放| 国产成人在线播放| 精品国产欧美一区二区五十路| 91理论片午午论夜理片久久| 欧美成年人视频网站欧美| 精品中文字幕在线观看| 91久久中文字幕| 久久久精品中文字幕| 97久久精品在线| xvideos亚洲人网站| 欧美一级在线亚洲天堂| 国产精品第一视频| 欧美午夜精品久久久久久久| 日韩精品视频在线播放| 91精品国产成人| 日韩精品亚洲精品| 亚洲第一精品夜夜躁人人爽| 久久精品国产视频| 亚洲自拍中文字幕| 国产精品久久久久久亚洲影视| 国产精品亚洲一区二区三区| 欧美久久久精品| 日韩欧美中文在线| 免费91麻豆精品国产自产在线观看| 中文字幕国产亚洲2019| 欧美大片大片在线播放| 国产高清在线不卡| 国产成人亚洲综合| 日韩动漫免费观看电视剧高清| 国产综合久久久久久| 久久久噜噜噜久久中文字免| 高清在线视频日韩欧美| 亚洲成人av片| 亚洲电影在线观看| 日韩精品免费视频| 欧美性在线观看| 欧美激情在线观看| 国产日韩av在线| 欧美在线xxx| 亚洲第一精品福利| 欧美日韩一区二区免费在线观看| 正在播放欧美视频| 国内精品小视频| 日本韩国欧美精品大片卡二| www亚洲精品| 亚洲男人天天操| 国产在线视频欧美| 久久久久久久国产| 韩国三级电影久久久久久| 色www亚洲国产张柏芝| 国产精品羞羞答答| 98精品国产自产在线观看| 国产精品久久激情| 国内精品国产三级国产在线专| 2019精品视频| 欧美小视频在线观看| 亚洲精品一区久久久久久| 精品爽片免费看久久| 日韩av电影院| 欧美激情视频在线免费观看 欧美视频免费一| 欧美日韩精品国产| 日韩视频免费大全中文字幕| 精品久久香蕉国产线看观看亚洲| 91久久久久久久| 91在线中文字幕| 国产精品视频一| 亚洲欧美精品一区| 成人av资源在线播放| 国产成人精品一区| 国产精品91在线| 久久视频精品在线| 日韩免费中文字幕| 欧美精品在线免费观看| 久久成年人视频| 91热精品视频| 久久视频免费观看| 久久久人成影片一区二区三区观看| 欧美综合在线观看| 91在线视频精品| 国产日韩在线免费| 中文国产亚洲喷潮| 久久久噜久噜久久综合| 国产精品久久精品| 亚洲欧美成人在线| 国产美女主播一区| 日本在线观看天堂男亚洲| 亚洲欧美制服综合另类| 2018中文字幕一区二区三区| 91精品久久久久久久久青青| 中文字幕亚洲专区| 国产成人一区二区三区小说| 久久精品国产久精国产一老狼| 亚洲a中文字幕| 久久久欧美一区二区| 欧美超级乱淫片喷水| 亚洲激情在线观看视频免费| 日韩国产高清污视频在线观看| 九九精品视频在线| 成人午夜高潮视频| 尤物yw午夜国产精品视频明星| 成人字幕网zmw| 欧美日韩国产成人高清视频| 亚洲精品久久久久| 日韩成人免费视频| 久久精品成人一区二区三区| 亚洲精品自拍第一页| 亚洲色图综合久久| 国内精品久久久久久影视8| 国产在线观看一区二区三区| 国产高清在线不卡| 国产成人拍精品视频午夜网站| 国产成人短视频| 欧美高清在线视频观看不卡| 亚洲天堂成人在线视频| 亚洲韩国日本中文字幕| 欧美国产日韩二区| 亚洲第一综合天堂另类专| 色诱女教师一区二区三区| 欧美日本亚洲视频| 久久资源免费视频| 国产丝袜一区二区三区| 欧美国产欧美亚洲国产日韩mv天天看完整| 国产不卡av在线免费观看| 欧美激情视频给我| 欧美性生活大片免费观看网址| 欧美一区三区三区高中清蜜桃| 欧美性高潮在线| 精品国产乱码久久久久久婷婷| 亚洲成人999| 亚洲一区二区中文字幕| 亚洲free性xxxx护士hd| 亚洲一区二区中文字幕| 一区二区av在线| 日韩精品免费在线视频| 国产精品久久久久久久久久久久| 久久久久久久久久久91| 久久精品这里热有精品| 亚洲欧美日韩一区二区三区在线| 国产精品精品视频一区二区三区| 国产精品免费一区| 欧美日韩日本国产| 欧美华人在线视频| 国产精品日日做人人爱| 欧美交受高潮1| 国产精品一区二区三区毛片淫片| 久久成人在线视频| 91精品在线影院| 久久精品电影一区二区|