1. 程式人生 > >多個單列索引和聯合索引的區別詳解

多個單列索引和聯合索引的區別詳解

背景:
為了提高資料庫效率,建索引是家常便飯;那麼當查詢條件為2個及以上時,我們是建立多個單列索引還是建立一個聯合索引好呢?他們之間的區別是什麼?哪個效率高呢?我在這裡詳細測試分析下。

一、聯合索引測試

注:Mysql版本為 5.7.20

建立測試表(表記錄數為63188):

CREATE TABLE `t_mobilesms_11` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `userId` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '使用者id,建立任務時的userid'
, `mobile` varchar(24) NOT NULL DEFAULT '' COMMENT '手機號碼', `billMonth` varchar(32) DEFAULT NULL COMMENT '賬單月', `time` varchar(32) DEFAULT NULL COMMENT '收/發簡訊時間', `peerNumber` varchar(64) NOT NULL COMMENT '對方號碼', `location` varchar(64) DEFAULT NULL COMMENT '通訊地(自己的)', `sendType` varchar(16
) DEFAULT NULL COMMENT 'SEND-傳送; RECEIVE-收取', `msgType` varchar(8) DEFAULT NULL COMMENT 'SMS-簡訊; MSS-彩信', `serviceName` varchar(256) DEFAULT NULL COMMENT '業務名稱. e.g. 點對點(網內)', `fee` int(11) DEFAULT NULL COMMENT '通訊費(單位分)', `createTime` datetime DEFAULT NULL COMMENT '建立時間', `lastModifyTime`
datetime DEFAULT NULL COMMENT '最後修改時間', PRIMARY KEY (`id`), KEY `聯合索引` (`userId`,`mobile`,`billMonth`) ) ENGINE=InnoDB AUTO_INCREMENT=71185 DEFAULT CHARSET=utf8 COMMENT='手機簡訊詳情'

我們為userId, mobile, billMonth三個欄位新增上聯合索引!

我們選擇 explain 檢視執行計劃來觀察索引利用情況:

1.查詢條件為 userid

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222'

這裡寫圖片描述

可以通過key看到,聯合索引有效

2.查詢條件為 mobile

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972'

這裡寫圖片描述
可以看到聯合索引無效

3.查詢條件為 billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE billMonth='2018-04'

這裡寫圖片描述
聯合索引無效

4.查詢條件為 userid and mobile

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND mobile='13281899972'

這裡寫圖片描述
聯合索引有效

5.查詢條件為 mobile and userid

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE  mobile='13281899972' AND userid='2222' 

這裡寫圖片描述
在4的基礎上調換了查詢條件的順序,發現聯合索引依舊有效

6.查詢條件為 userid or mobile

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' OR mobile='13281899972'

這裡寫圖片描述
and 換成 or,發現聯合所索引無效

7.查詢條件為 userid and billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND billMonth='2018-04'

這裡寫圖片描述
這兩個條件分別位於聯合索引位置的第一和第三,測試聯合索引依舊有效

8.查詢條件為 mobile and billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972' AND billMonth='2018-04'

這裡寫圖片描述
這兩個條件分別位於聯合索引位置的第二和第三,發現聯合索引無效

9.查詢條件為 userid and mobile and billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE  userid='2222' AND mobile='13281899972' AND billMonth='2018-04'

這裡寫圖片描述
所有條件一起查詢,聯合索引有效!(當然,這才是最正統的用法啊!)

二、單列索引測試

建立三個單列索引:
這裡寫圖片描述

1.查詢條件為 userid and mobile and billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE  userid='2222' AND mobile='13281899972' AND billMonth='2018-04'

這裡寫圖片描述
我們發現三個單列索引只有 userid 有效(位置為查詢條件第一個),其他兩個都沒有用上

2.查詢條件為 mobile and billMonth

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972' AND billMonth='2018-04'

這裡寫圖片描述
我們發現此處兩個查詢條件只有 mobile 有效(位置也為查詢條件第一個),後面的無效

3.查詢條件為 userid or mobile

EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE  userid='2222' OR mobile='13281899972' 

這裡寫圖片描述
這次把 and 換成 or,發現兩個查詢條件都用上索引了!

三、結論

通俗理解:
利用索引中的附加列,您可以縮小搜尋的範圍,但使用一個具有兩列的索引 不同於使用兩個單獨的索引。複合索引的結構與電話簿類似,人名由姓和名構成,電話簿首先按姓氏對進行排序,然後按名字對有相同姓氏的人進行排序。如果您知道姓,電話簿將非常有用;如果您知道姓和名,電話簿則更為有用,但如果您只知道名不姓,電話簿將沒有用處

所以說建立複合索引時,應該仔細考慮列的順序。對索引中的所有列執行搜尋或僅對前幾列執行搜尋時,複合索引非常有用僅對後面的任意列執行搜尋時,複合索引則沒有用處。

重點:

多個單列索引多條件查詢時只會生效第一個索引!所以多條件聯合查詢時最好建聯合索引!

最左字首原則:

顧名思義是最左優先,以最左邊的為起點任何連續的索引都能匹配上,
注:如果第一個欄位是範圍查詢需要單獨建一個索引
注:在建立聯合索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。這樣的話擴充套件性較好,比如 userid 經常需要作為查詢條件,而 mobile 不常常用,則需要把 userid 放在聯合索引的第一位置,即最左邊

同時存在聯合索引和單列索引(欄位有重複的),這個時候查詢mysql會怎麼用索引呢?

這個涉及到mysql本身的查詢優化器策略了,當一個表有多條索引可走時, Mysql 根據查詢語句的成本來選擇走哪條索引;

有人說where查詢是按照從左到右的順序,所以篩選力度大的條件儘量放前面。網上百度過,很多都是這種說法,但是據我研究,mysql執行優化器會對其進行優化當不考慮索引時,where條件順序對效率沒有影響真正有影響的是是否用到了索引

聯合索引本質:

當建立(a,b,c)聯合索引時,相當於建立了(a)單列索引(a,b)聯合索引以及(a,b,c)聯合索引
想要索引生效的話,只能使用 a和a,b和a,b,c三種組合;當然,我們上面測試過,a,c組合也可以,但實際上只用到了a的索引,c並沒有用到!
注:這個可以結合上邊的 通俗理解 來思考!

其他知識點:

1、需要加索引的欄位,要在where條件中
2、資料量少的欄位不需要加索引;因為建索引有一定開銷,如果資料量小則沒必要建索引(速度反而慢)
3、如果where條件中是OR關係,加索引不起作用
4、聯合索引比對每個列分別建索引更有優勢,因為索引建立得越多就越佔磁碟空間,在更新資料的時候速度會更慢。另外建立多列索引時,順序也是需要注意的,應該將嚴格的索引放在前面,這樣篩選的力度會更大,效率更高