单列索引和联合索引,有什么区别?
作者:深寒丶
來源:blog.csdn.net/abysscarry/article/details/80792876
背景:
為了提高數(shù)據(jù)庫(kù)效率,建索引是家常便飯;那么當(dāng)查詢條件為 2 個(gè)及以上時(shí),我們是創(chuàng)建多個(gè)單列索引還是創(chuàng)建一個(gè)聯(lián)合索引好呢?他們之間的區(qū)別是什么?哪個(gè)效率高呢?我在這里詳細(xì)測(cè)試分析下。
一、聯(lián)合索引測(cè)試
注:Mysql 版本為 5.7.20
創(chuàng)建測(cè)試表(表記錄數(shù)為 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,創(chuàng)建任務(wù)時(shí)的userid',`mobile` varchar(24) NOT NULL DEFAULT '' COMMENT '手機(jī)號(hào)碼',`billMonth` varchar(32) DEFAULT NULL COMMENT '賬單月',`time` varchar(32) DEFAULT NULL COMMENT '收/發(fā)短信時(shí)間',`peerNumber` varchar(64) NOT NULL COMMENT '對(duì)方號(hào)碼',`location` varchar(64) DEFAULT NULL COMMENT '通信地(自己的)',`sendType` varchar(16) DEFAULT NULL COMMENT 'SEND-發(fā)送; RECEIVE-收取',`msgType` varchar(8) DEFAULT NULL COMMENT 'SMS-短信; MSS-彩信',`serviceName` varchar(256) DEFAULT NULL COMMENT '業(yè)務(wù)名稱. e.g. 點(diǎn)對(duì)點(diǎn)(網(wǎng)內(nèi))',`fee` int(11) DEFAULT NULL COMMENT '通信費(fèi)(單位分)',`createTime` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',`lastModifyTime` datetime DEFAULT NULL COMMENT '最后修改時(shí)間',PRIMARY KEY (`id`),KEY `聯(lián)合索引` (`userId`,`mobile`,`billMonth`) ) ENGINE=InnoDB AUTO_INCREMENT=71185 DEFAULT CHARSET=utf8 COMMENT='手機(jī)短信詳情'我們?yōu)閡serId,?mobile,?billMonth三個(gè)字段添加上聯(lián)合索引!
我們選擇?explain?查看執(zhí)行計(jì)劃來觀察索引利用情況:
1.查詢條件為?userid
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222'可以通過key看到,聯(lián)合索引有效
2.查詢條件為?mobile
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972'可以看到聯(lián)合索引無效
3.查詢條件為?billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE billMonth='2018-04'聯(lián)合索引無效
4.查詢條件為?userid and mobile
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND mobile='13281899972'聯(lián)合索引有效
5.查詢條件為?mobile and userid
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972' AND userid='2222'在 4 的基礎(chǔ)上調(diào)換了查詢條件的順序,發(fā)現(xiàn)聯(lián)合索引依舊有效
6.查詢條件為?userid or mobile
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' OR mobile='13281899972'把?and?換成?or,發(fā)現(xiàn)聯(lián)合所索引無效!
7.查詢條件為?userid and billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND billMonth='2018-04'這兩個(gè)條件分別位于聯(lián)合索引位置的第一和第三,測(cè)試聯(lián)合索引依舊有效!
8.查詢條件為?mobile and billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972' AND billMonth='2018-04'這兩個(gè)條件分別位于聯(lián)合索引位置的第二和第三,發(fā)現(xiàn)聯(lián)合索引無效!
9.查詢條件為?userid and mobile and billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND mobile='13281899972' AND billMonth='2018-04'所有條件一起查詢,聯(lián)合索引有效!(當(dāng)然,這才是最正統(tǒng)的用法啊!)
二、單列索引測(cè)試
創(chuàng)建三個(gè)單列索引:
1.查詢條件為?userid and mobile and billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' AND mobile='13281899972' AND billMonth='2018-04'我們發(fā)現(xiàn)三個(gè)單列索引只有?userid?有效(位置為查詢條件第一個(gè)),其他兩個(gè)都沒有用上。
那么為什么沒有用上呢?按照我們的理解,三個(gè)字段都加索引了,無論怎么排列組合查詢,應(yīng)該都能利用到這三個(gè)索引才對(duì)!
其實(shí)這里其實(shí)涉及到了 mysql 優(yōu)化器的優(yōu)化策略!當(dāng)多條件聯(lián)合查詢時(shí),優(yōu)化器會(huì)評(píng)估用哪個(gè)條件的索引效率最高!它會(huì)選擇最佳的索引去使用,也就是說,此處userid 、mobile 、billMonth這三個(gè)索引列都能用,只不過優(yōu)化器判斷只需要使用userid這一個(gè)索引就能完成本次查詢,故最終 explain 展示的 key 為 userid。
當(dāng)然,如果優(yōu)化器判斷本次查詢非要全使用三個(gè)索引才能效率最高,那么 explain 的 key 就會(huì)是userid 、mobile 、billMonth,都會(huì)生效!
2.查詢條件為?mobile and billMonth
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE mobile='13281899972' AND billMonth='2018-04'我們發(fā)現(xiàn)此處兩個(gè)查詢條件只有?mobile?生效(位置也為查詢條件第一個(gè))
3.查詢條件為?userid or mobile
EXPLAIN SELECT * FROM `t_mobilesms_11` WHERE userid='2222' OR mobile='13281899972'這次把?and?換成?or,發(fā)現(xiàn)兩個(gè)查詢條件都用上索引了!
我們?cè)诰W(wǎng)上可能常常看到有人說 or 會(huì)導(dǎo)致索引失效,其實(shí)這并不準(zhǔn)確。而且我們首先需要判斷用的是哪個(gè)數(shù)據(jù)庫(kù)哪個(gè)版本,什么引擎?
比如我用的是 mysql5.7 版本,innodb 引擎,在這個(gè)環(huán)境下我們?cè)偃ビ懻撍饕木唧w問題。
關(guān)于 or 查詢的真相是:
所謂的索引失效指的是:假如 or 連接的倆個(gè)查詢條件字段中有一個(gè)沒有索引的話,引擎會(huì)放棄索引而產(chǎn)生全表掃描。我們從 or 的基本含義出發(fā)應(yīng)該能理解并認(rèn)可這種說法,沒啥問題。
此刻需要注意type類型為index_merge。
我查資料說 mysql 5.0 版本之前 使用 or只會(huì)用到一個(gè)索引(即使如上我給 userid 和 mobile 都建立的單列索引),但自從 5.0 版本開始引入了index_merge 索引合并優(yōu)化!也就是說,我們現(xiàn)在可以利用上多個(gè)索引去優(yōu)化 or 查詢了。
index_merge 作用:
1、索引合并是把幾個(gè)索引的范圍掃描合并成一個(gè)索引。
2、索引合并的時(shí)候,會(huì)對(duì)索引進(jìn)行并集,交集或者先交集再并集操作,以便合并成一個(gè)索引。
3、這些需要合并的索引只能是一個(gè)表的。不能對(duì)多表進(jìn)行索引合并。
index_merge 應(yīng)用場(chǎng)景:
1.對(duì) OR 語句求并集,如查詢SELECT * FROM TB1 WHERE c1="xxx" OR c2=""xxx"時(shí),如果 c1 和 c2 列上分別有索引,可以按照 c1 和 c2 條件進(jìn)行查詢,再將查詢結(jié)果合并(union)操作,得到最終結(jié)果
2.對(duì) AND 語句求交集,如查詢SELECT * FROM TB1 WHERE c1="xxx" AND c2=""xxx"時(shí),如果 c1 和 c2 列上分別有索引,可以按照 c1 和 c2 條件進(jìn)行查詢,再將查詢結(jié)果取交集(intersect)操作,得到最終結(jié)果
3.對(duì) AND 和 OR 組合語句求結(jié)果
三、結(jié)論
通俗理解:
利用索引中的附加列,您可以縮小搜索的范圍,但使用一個(gè)具有兩列的索引 不同于使用兩個(gè)單獨(dú)的索引。復(fù)合索引的結(jié)構(gòu)與電話簿類似,人名由姓和名構(gòu)成,電話簿首先按姓氏對(duì)進(jìn)行排序,然后按名字對(duì)有相同姓氏的人進(jìn)行排序。如果您知道姓,電話簿將非常有用;如果您知道姓和名,電話簿則更為有用,但如果您只知道名不姓,電話簿將沒有用處。
所以說創(chuàng)建復(fù)合索引時(shí),應(yīng)該仔細(xì)考慮列的順序。對(duì)索引中的所有列執(zhí)行搜索或僅對(duì)前幾列執(zhí)行搜索時(shí),復(fù)合索引非常有用;僅對(duì)后面的任意列執(zhí)行搜索時(shí),復(fù)合索引則沒有用處。
重點(diǎn):
多個(gè)單列索引在多條件查詢時(shí)優(yōu)化器會(huì)選擇最優(yōu)索引策略,可能只用一個(gè)索引,也可能將多個(gè)索引全用上!?但多個(gè)單列索引底層會(huì)建立多個(gè) B+索引樹,比較占用空間,也會(huì)浪費(fèi)一定搜索效率,故如果只有多條件聯(lián)合查詢時(shí)最好建聯(lián)合索引!
最左前綴原則:
顧名思義是最左優(yōu)先,以最左邊的為起點(diǎn)任何連續(xù)的索引都能匹配上, 注:如果第一個(gè)字段是范圍查詢需要單獨(dú)建一個(gè)索引注:在創(chuàng)建聯(lián)合索引時(shí),要根據(jù)業(yè)務(wù)需求,where 子句中使用最頻繁的一列放在最左邊。這樣的話擴(kuò)展性較好,比如?userid?經(jīng)常需要作為查詢條件,而?mobile?不常常用,則需要把?userid?放在聯(lián)合索引的第一位置,即最左邊
同時(shí)存在聯(lián)合索引和單列索引(字段有重復(fù)的),這個(gè)時(shí)候查詢 mysql 會(huì)怎么用索引呢?
這個(gè)涉及到 mysql 本身的查詢優(yōu)化器策略了,當(dāng)一個(gè)表有多條索引可走時(shí), Mysql 根據(jù)查詢語句的成本來選擇走哪條索引;
有人說 where 查詢是按照從左到右的順序,所以篩選力度大的條件盡量放前面。網(wǎng)上百度過,很多都是這種說法,但是據(jù)我研究,mysql 執(zhí)行優(yōu)化器會(huì)對(duì)其進(jìn)行優(yōu)化,當(dāng)不考慮索引時(shí),where 條件順序?qū)π蕸]有影響,真正有影響的是是否用到了索引!
聯(lián)合索引本質(zhì):
當(dāng)創(chuàng)建**(a,b,c)聯(lián)合索引時(shí),相當(dāng)于創(chuàng)建了(a)單列索引**,(a,b)聯(lián)合索引以及**(a,b,c)聯(lián)合索引** 想要索引生效的話,只能使用 a 和 a,b 和 a,b,c 三種組合;當(dāng)然,我們上面測(cè)試過,a,c 組合也可以,但實(shí)際上只用到了 a 的索引,c 并沒有用到!注:這個(gè)可以結(jié)合上邊的 通俗理解 來思考!
其他知識(shí)點(diǎn):
1、需要加索引的字段,要在 where 條件中
2、數(shù)據(jù)量少的字段不需要加索引;因?yàn)?strong>建索引有一定開銷,如果數(shù)據(jù)量小則沒必要建索引(速度反而慢)
3、避免在 where 子句中使用or來連接條件,因?yàn)槿绻?strong>倆個(gè)字段中有一個(gè)沒有索引的話,引擎會(huì)放棄索引而產(chǎn)生全表掃描
4、聯(lián)合索引比對(duì)每個(gè)列分別建索引更有優(yōu)勢(shì),因?yàn)樗饕⒌迷蕉嗑驮秸即疟P空間,在更新數(shù)據(jù)的時(shí)候速度會(huì)更慢。另外建立多列索引時(shí),順序也是需要注意的,應(yīng)該將嚴(yán)格的索引放在前面,這樣篩選的力度會(huì)更大,效率更高。
最后的說明:
網(wǎng)上關(guān)于索引優(yōu)化等文章太多了,針對(duì)各個(gè)數(shù)據(jù)庫(kù)各個(gè)版本各種引擎都可能存在不一樣的說法!
我們的 SQL 引擎自帶的優(yōu)化也越來越強(qiáng)大,說不定你的某個(gè) SQL 優(yōu)化認(rèn)知,其 SQL 引擎在某次升級(jí)中早就自優(yōu)化了。
所以要么跟進(jìn)官方文檔,要么關(guān)注數(shù)據(jù)庫(kù)大牛的最新文章,要么在現(xiàn)有數(shù)據(jù)庫(kù)環(huán)境下自己去親手測(cè)試!
總結(jié)
以上是生活随笔為你收集整理的单列索引和联合索引,有什么区别?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 同事把 Redis用成这鬼样子,真坑!
- 下一篇: 住手!!你不需要微服务!