轉自http://www.javashuo.com/article/p-dcmefepu-bd.htmlmysql
背景:
爲了提升數據庫效率,建索引是屢見不鮮;那麼當查詢條件爲2個及以上時,咱們是建立多個單列索引仍是建立一個聯合索引好呢?他們之間的區別是什麼?哪一個效率高呢?我在這裏詳細測試分析下。sql
注:Mysql版本爲 5.7.20數據庫
建立測試表(表記錄數爲63188):markdown
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
三個字段添加上聯合索引!mysql優化
咱們選擇 explain
查看執行計劃來觀察索引利用狀況:測試
1.查詢條件爲 userid
優化
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222'
能夠經過key
看到,聯合索引有效atom
2.查詢條件爲 mobile
.net
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972'
能夠看到聯合索引無效code
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
有效(位置爲查詢條件第一個),其餘兩個都沒有用上。
那麼爲何沒有用上呢?按照咱們的理解,三個字段都加索引了,不管怎麼排列組合查詢,應該都能利用到這三個索引纔對!
其實這裏其實涉及到了mysql優化器的優化策略!當多條件聯合查詢時,優化器會評估用哪一個條件的索引效率最高!它會選擇最佳的索引去使用,也就是說,此處userid 、mobile 、billMonth
這三個索引列都能用,只不過優化器判斷只須要使用userid
這一個索引就能完成本次查詢,故最終explain展現的key爲userid。
固然,若是優化器判斷本次查詢非要全使用三個索引才能效率最高,那麼explain的key就會是userid 、mobile 、billMonth
,都會生效!
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
,發現兩個查詢條件都用上索引了!
通俗理解:
利用索引中的附加列,您能夠縮小搜索的範圍,但使用一個具備兩列的索引 不一樣於使用兩個單獨的索引。複合索引的結構與電話簿相似,人名由姓和名構成,電話簿首先按姓氏對進行排序,而後按名字對有相同姓氏的人進行排序。若是您知道姓,電話簿將很是有用;若是您知道姓和名,電話簿則更爲有用,但若是您只知道名不姓,電話簿將沒有用處。
因此說建立複合索引時,應該仔細考慮列的順序。對索引中的全部列執行搜索或僅對前幾列執行搜索時,複合索引很是有用;僅對後面的任意列執行搜索時,複合索引則沒有用處。
重點:
多個單列索引在多條件查詢時優化器會選擇最優索引策略,可能只用一個索引,也可能將多個索引全用上! 但多個單列索引底層會創建多個B+索引樹,比較佔用空間,也會浪費必定搜索效率,故若是隻有多條件聯合查詢時最好建聯合索引!
最左前綴原則:
顧名思義是最左優先,以最左邊的爲起點任何連續的索引都能匹配上,
注:若是第一個字段是範圍查詢須要單獨建一個索引
注:在建立聯合索引時,要根據業務需求,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並無用到!
注:這個能夠結合上邊的 通俗理解 來思考!
其餘知識點:
一、須要加索引的字段,要在where條件中
二、數據量少的字段不須要加索引;由於建索引有必定開銷,若是數據量小則不必建索引(速度反而慢)
三、若是where條件中是OR關係,加索引不起做用
四、聯合索引比對每一個列分別建索引更有優點,由於索引創建得越多就越佔磁盤空間,在更新數據的時候速度會更慢。另外創建多列索引時,順序也是須要注意的,應該將嚴格的索引放在前面,這樣篩選的力度會更大,效率更高。