干貨篇:一篇文章讓你——《深入解析MySQL索引原理 》

概述

最近一段時(shí)間重新深入研究了一遍MySQL的內(nèi)容姑曙,今天主要分享分析MySQL索引原理襟交,后續(xù)會(huì)輸出一些關(guān)于MySQL方面的干貨,希望各位小伙伴喜歡渣磷。

一婿着、什么是索引、為什么要建立索引醋界?

關(guān)于索引的理解,個(gè)人更加喜歡將其比喻為字典里面的目錄提完,根據(jù)字典來進(jìn)行查詢的速度遠(yuǎn)大于每一頁逐個(gè)逐個(gè)字排查的速度形纺。

? ? 索引主要用于快速找出在某個(gè)列中有特定值的行,倘若不使用索引徒欣,MySQL必須從第一條記錄開始讀完整個(gè)表逐样,直到找出相關(guān)的行,表越大,查詢數(shù)據(jù)所花費(fèi)的時(shí)間就越多脂新。如果表中查詢的列有一個(gè)索引挪捕,MySQL能夠快速到達(dá)一個(gè)位置去搜索數(shù)據(jù),而不必查找所有數(shù)據(jù)争便,那么將會(huì)節(jié)省很大一部分時(shí)間级零。

(一)、索引的存儲(chǔ)文件是如何的滞乙?

首先我們來看看mysql索引數(shù)據(jù)的存儲(chǔ)位置:

mysql的數(shù)據(jù)存儲(chǔ)是存放在mysql的data文件夾底下奏纪,截圖如下所示:

? ? ? 查看該文件夾底下,我們通痴镀簦可以看到相關(guān)的信息內(nèi)容:

.frm表結(jié)構(gòu)文件序调,和所選用的存儲(chǔ)引擎無關(guān)。

(二)兔簇、MyISAM引擎的文件格式

1发绢、myd?數(shù)據(jù)文件后綴

2、myi??索引文件后綴

(三)垄琐、InnoDB引擎的文件格式

1朴摊、ibd?將索引信息和數(shù)據(jù)一起存儲(chǔ)起來

二、索引的結(jié)構(gòu)是什么此虑?為何不采用別的結(jié)構(gòu)甚纲?

?? ? 索引在起初做設(shè)計(jì)的時(shí)候其實(shí)是有一定數(shù)據(jù)結(jié)構(gòu)選型的,對(duì)于不同的數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)朦前,我做了以下的相關(guān)總結(jié):

(一)介杆、使用二叉樹作為索引結(jié)構(gòu)缺陷

容易導(dǎo)致二叉樹出現(xiàn)結(jié)構(gòu)偏移,極端情況容易變成一條鏈表的形狀韭寸。

例如下方情況:

(二)春哨、使用紅黑樹數(shù)據(jù)結(jié)構(gòu)的缺陷

紅黑樹雖然有自動(dòng)進(jìn)行樹節(jié)點(diǎn)的二叉平衡功能。雖然相對(duì)于二叉樹而言恩伺,不會(huì)有太嚴(yán)重的單邊偏移情況赴背,但還是避免不了極端情況下樹的重心出現(xiàn)偏移的現(xiàn)象。(數(shù)據(jù)量變大的情況下晶渠,深度會(huì)變大)

使用紅黑樹數(shù)據(jù)結(jié)構(gòu)容易在極端的情況下發(fā)生紅黑樹失重情況凰荚,如下圖所示,隨著數(shù)據(jù)量的增大褒脯,失重情況愈發(fā)嚴(yán)重:


(三)便瑟、hash索引的缺陷

雖然說hash查詢的速度很快,但是依然有以下缺陷:

1番川、無法解決查詢范圍導(dǎo)致的問題

? 2到涂、無法解決hash沖突的問題

三脊框、如何理解BTree

在學(xué)習(xí)索引的真正結(jié)構(gòu)之前,首先我們需要來了解一下什么是BTree践啄。

(一)浇雹、BTree的基本結(jié)構(gòu)如下圖所示:

(二)、BTree里面包含有幾個(gè)重要的特點(diǎn)

? ? 1屿讽、度(Degree)-節(jié)點(diǎn)的數(shù)據(jù)存儲(chǔ)個(gè)數(shù)昭灵,一旦存儲(chǔ)數(shù)據(jù)超過度值就要進(jìn)行節(jié)點(diǎn)分裂

? ? 2、 所有葉子節(jié)點(diǎn)具有相同的深度

? ? 3聂儒、葉子節(jié)點(diǎn)的指針為空

? ? 4虎锚、葉子節(jié)點(diǎn)中的數(shù)據(jù)key從左到右遞增排列

btree數(shù)據(jù)結(jié)構(gòu)通過節(jié)點(diǎn)的橫向擴(kuò)展,從而壓低整個(gè)Btree的高度衩婚,減少了節(jié)點(diǎn)io讀取的次數(shù)窜护。通常百萬級(jí)別的數(shù)據(jù)會(huì)被壓到3-5層的高度。

四非春、節(jié)點(diǎn)數(shù)據(jù)讀取過程發(fā)生了什么柱徙?

我在初學(xué)索引的時(shí)候,覺得io次數(shù)讀取越少奇昙,因此獲取數(shù)據(jù)的速度會(huì)越快护侮。后來重新回看了一遍操作系統(tǒng)原理的講解之后,對(duì)于這塊的內(nèi)容有了更加深入的認(rèn)識(shí)储耐。

(一)羊初、磁盤數(shù)據(jù)讀取

指針在同一磁道上邊讀取數(shù)據(jù)的時(shí)候如果采用的是順序讀取方式,那么數(shù)據(jù)的讀取效率就會(huì)比較高效什湘。倘若數(shù)據(jù)在進(jìn)行讀取的時(shí)候采用了隨機(jī)讀取的方式长赞,則需要進(jìn)行磁道的變換,這種磁道變換讀取所帶來的性能消耗會(huì)是巨大的闽撤。每個(gè)節(jié)點(diǎn)存放的地址通常都不會(huì)是連續(xù)的得哆,不可避免的會(huì)有一些內(nèi)存碎片存在,因此每一次進(jìn)行io讀取都會(huì)是比較耗費(fèi)性能的哟旗。

btree里面的節(jié)點(diǎn)采用了key-value的基本存儲(chǔ)結(jié)構(gòu)贩据,key是索引的數(shù)值,value是存儲(chǔ)的data數(shù)值闸餐。由于我們對(duì)于btree進(jìn)行節(jié)點(diǎn) 比較的時(shí)候是基于內(nèi)存進(jìn)行數(shù)據(jù)比較饱亮,先從磁盤進(jìn)行io讀取數(shù)據(jù),讀取到cpu緩存中進(jìn)行比對(duì)绎巨。

在了解了磁道讀取問題之后近尚,我們?cè)賮硭伎家幌滤饕谋闅v。

如下圖所示:

? ? 如圖所示场勤,假設(shè)我們沒有采用索引戈锻,要讀取數(shù)據(jù)第七行(23),則需要進(jìn)行7次的io讀取操作和媳。假若使用了索引格遭,則可以通過走索引的方式,34-->22-->23一共三次io讀取操作即可查找到所要的節(jié)點(diǎn)23留瞳。

(二)拒迅、度值無限擴(kuò)增會(huì)如何?

如果我們將節(jié)點(diǎn)的度設(shè)置到極致她倘,例如說將度設(shè)置到100W璧微,那么BTree的高度就會(huì)降低,查詢的次數(shù)就會(huì)大大減少硬梁,是否可行前硫?

這種想法是不可行的,節(jié)點(diǎn)會(huì)變得過大荧止。每次進(jìn)行節(jié)點(diǎn)數(shù)據(jù)讀取的時(shí)候都需要將磁盤的數(shù)據(jù)加載到操作系統(tǒng)自身的緩存中屹电。假設(shè)將度值設(shè)置過大,io一次讀取的大小還是有限跃巡,過大的節(jié)點(diǎn)還是需要進(jìn)行多次的io讀取危号。

計(jì)算機(jī)里面的內(nèi)存和磁盤之間進(jìn)行數(shù)據(jù)讀取是按照計(jì)算機(jī)自身標(biāo)準(zhǔn)來進(jìn)行設(shè)置的,每次讀取的大小空間基本單位通常稱之為素邪,假設(shè)每一頁的數(shù)據(jù)讀取通常是8k(不同硬件設(shè)備大小不同)外莲,假若說一個(gè)節(jié)點(diǎn)的大小為10M,那么讀取該節(jié)點(diǎn)里面的數(shù)據(jù)則需要進(jìn)行多次的io操作。(通常DBA在進(jìn)行數(shù)據(jù)庫的度值設(shè)置的時(shí)候默認(rèn)是將一個(gè)頁單位的大小設(shè)置為度的大小兔朦。)

(三)偷线、磁盤 IO

一個(gè)數(shù)據(jù)庫必須保證其中存儲(chǔ)的所有數(shù)據(jù)都是可以隨時(shí)讀寫的,同時(shí)因?yàn)?b> MySQL 中所有的數(shù)據(jù)其實(shí)都是以文件的形式存儲(chǔ)在磁盤上的烘绽,而從磁盤上隨機(jī)訪問對(duì)應(yīng)的數(shù)據(jù)非常耗時(shí)淋昭,所以數(shù)據(jù)庫程序和操作系統(tǒng)提供了 緩沖池 內(nèi)存以提高數(shù)據(jù)的訪問速度。

除此之外安接,我們知道數(shù)據(jù)庫對(duì)數(shù)據(jù)的讀取并不是以行為單位進(jìn)行的翔忽,無論是讀取一行還是多行,都會(huì)將該行或者多行所在的頁全部加載進(jìn)來盏檐,然后再讀取對(duì)應(yīng)的數(shù)據(jù)記錄歇式;也就是說,讀取所耗費(fèi)的時(shí)間與行數(shù)無關(guān)胡野,只與頁數(shù)有關(guān)材失。(這也就是數(shù)據(jù)庫的預(yù)讀取機(jī)制)

? 頁大小可以如下操作顯示:

在 MySQL 中,頁的大小一般為 16KB硫豆,不過也可能是 8KB龙巨、32KB 或者其他值笼呆,這跟 MySQL 的存儲(chǔ)引擎對(duì)數(shù)據(jù)的存儲(chǔ)方式有很大的關(guān)系。

關(guān)于數(shù)據(jù)庫的緩存池原理剖析日后在后續(xù)我會(huì)進(jìn)行解析旨别,這里暫時(shí)先略過诗赌。

五、如何進(jìn)行BTree的優(yōu)化秸弛?

如果每個(gè)節(jié)點(diǎn)的大小越小铭若,則操作系統(tǒng)讀取一頁數(shù)據(jù)的內(nèi)容則越多,內(nèi)存中可容納的數(shù)據(jù)量增加递览,處理的效率也會(huì)有所提升叼屠。因此 B+Tree 就此誕生了。

(一)绞铃、如何理解B+Tree镜雨?

? B+Tree的基本結(jié)構(gòu)如下圖所示:

? (二)、B+Tree幾個(gè)比較重要的特點(diǎn):

? ? 1憎兽、非葉子節(jié)點(diǎn)不存儲(chǔ)data冷离,只存儲(chǔ)key,可以增大度的值纯命。

? ? 2西剥、葉子節(jié)點(diǎn)不存儲(chǔ)指針。

? 3亿汞、順序訪問指針瞭空,提高區(qū)間訪問的性能。

?????仔細(xì)觀察圖中樹結(jié)構(gòu)的同學(xué)不知道是否有發(fā)現(xiàn)疗我,B+Tree里面對(duì)于索引數(shù)據(jù)進(jìn)行了適當(dāng)?shù)娜哂啻鎯?chǔ)咆畏,但是這一點(diǎn)相比于度大小的增加而言,并不會(huì)帶來太多的性能影響吴裤。由于非葉子結(jié)點(diǎn)只存儲(chǔ)key旧找,并沒有存儲(chǔ)data數(shù)據(jù),因此所有的非葉子結(jié)點(diǎn)的度可以增加地更大麦牺, 使得一次 io 讀取的數(shù)據(jù)更多钮蛛,從磁盤讀取到操作系統(tǒng)內(nèi)存中的數(shù)據(jù)也大大增加。

仔細(xì)觀察B+Tree里面的數(shù)據(jù)結(jié)構(gòu)剖膳,會(huì)發(fā)現(xiàn)葉子節(jié)點(diǎn)里面有相應(yīng)的順序訪問指針魏颓。

? ? B+Tree的葉子節(jié)點(diǎn)之間的順序訪問指針的作用可以提高范圍查詢的效率。

例如說在一定范圍內(nèi)進(jìn)行數(shù)據(jù)查詢的時(shí)候不需要像BTree那樣吱晒,每次在進(jìn)行節(jié)點(diǎn)數(shù)據(jù)讀取的時(shí)候都需要重新從頂部開始讀取甸饱,可以根據(jù)相應(yīng)的順序指針直接進(jìn)行連續(xù)讀取,提高了范圍性讀取數(shù)據(jù)的效率。

六叹话、Myisam非聚簇索引

? ? ? 之前我們有說過mysql里面數(shù)據(jù)存儲(chǔ)的位置和文件信息偷遗。myisam存儲(chǔ)引擎里面,mysql讀取數(shù)據(jù)的時(shí)候會(huì)有一個(gè)文件指針渣刷,根據(jù)指針來讀取數(shù)據(jù)文件的順序?yàn)椋? myi--->.myd(讀取數(shù)據(jù)文件里面的內(nèi)容然后查找數(shù)據(jù))

? ? 存儲(chǔ)引擎里面的索引通常會(huì)被劃分為主鍵索引和非主鍵索引(一般是指除了主鍵索引之外的那些索引)

(一)鹦肿、主鍵索引:

主鍵索引比較好理解矗烛,B+Tree里面的葉子節(jié)點(diǎn)的key存儲(chǔ)的是索引值辅柴,value存儲(chǔ)的是相應(yīng)data數(shù)據(jù)的指針。

(二)瞭吃、輔助索引:

Myisam里面的輔助索引結(jié)構(gòu)和主鍵索引基本一致碌嘀,結(jié)構(gòu)如下圖所示

(三)、Innodb聚簇索引

在上邊我們提及過innodb存儲(chǔ)中歪架,是將數(shù)據(jù)和索引放在了同一份文件里面的股冗。

因此innodb里面的非主鍵索引(輔助索引)和主鍵索引有一定的區(qū)別,下邊我們來用圖例進(jìn)行詳細(xì)對(duì)比說明:

innodb里面的主鍵索引和非主鍵索引:

(四)和蚪、主鍵索引

主鍵索引里面的存儲(chǔ)情況如下圖所示止状,葉子節(jié)點(diǎn)里面的key是索引的信息,value是數(shù)據(jù)表里面該索引對(duì)應(yīng)行的數(shù)據(jù)信息攒霹。

(五)怯疤、非主鍵索引

這種索引在進(jìn)行存儲(chǔ)的時(shí)候,雖然說節(jié)點(diǎn)依然是key-value的結(jié)構(gòu)催束,key對(duì)應(yīng)的是相應(yīng)的索引集峦,但是value對(duì)應(yīng)的卻是主鍵索引的值。如下圖所示:

如果索引里面存儲(chǔ)的值不是數(shù)字類型而是字符串類型抠刺,那么在進(jìn)行索引插入或者索引查找的時(shí)候都需要通過計(jì)算每個(gè)字符的ASCII碼來判斷索引的大小塔淤。

七、為什么InnoDB表必須有主鍵速妖?

? ? ? 在innodb存儲(chǔ)引擎里面高蜂,數(shù)據(jù)的默認(rèn)的存儲(chǔ)結(jié)構(gòu)默認(rèn)是以B+Tree的結(jié)構(gòu)來進(jìn)行存儲(chǔ)的,因此必須要有主鍵存在罕容。

即使手動(dòng)沒有設(shè)置主鍵备恤,innodb也會(huì)自動(dòng)選取一列數(shù)據(jù)進(jìn)行設(shè)置,或者默認(rèn)生成一列作為主鍵杀赢。

(一)烘跺、使用uuid和自增id作為主鍵的區(qū)別?

在了解了索引原理之后脂崔,我們不妨從底層來思考一下使用uuid替代自增id帶來的缺陷滤淳。

1、由于uuid的長度普遍要比自增id的長度要長砌左,因此uuid更加占用磁盤空間脖咐。

2铺敌、uuid通常都是以字符串的形式存在的,因此在進(jìn)行比較的時(shí)候大多都是基于字符串的形式進(jìn)行比較的(使用ack||碼),而自增主鍵使用的是integer的比較方式屁擅。效率方面差異較大偿凭。

3、uuid在進(jìn)行數(shù)據(jù)插入的時(shí)候可能會(huì)插入到某個(gè)度已經(jīng)滿了的節(jié)點(diǎn)里面派歌,這個(gè)時(shí)候原有的舊節(jié)點(diǎn)就需要進(jìn)行節(jié)點(diǎn)的分裂操作了弯囊,需要進(jìn)行數(shù)據(jù)的移動(dòng),降低性能效率胶果。

(二)匾嘱、聯(lián)合索引的底層存儲(chǔ)結(jié)構(gòu)是如何的?

在實(shí)際項(xiàng)目開發(fā)中早抠,聯(lián)合索引是我們經(jīng)常會(huì)用到的優(yōu)化技巧之一霎烙,本文暫時(shí)先不講優(yōu)化,先從底層去理解聯(lián)合索引的存儲(chǔ)方式蕊连,后邊會(huì)展開優(yōu)化的講解悬垃。例如一個(gè)聯(lián)合索引里面用到了id,職位名稱甘苍,出生日期三個(gè)字段尝蠕,那么在存儲(chǔ)的時(shí)候會(huì)將三個(gè)字段一起存入節(jié)點(diǎn)里面的key位置。如下圖所示:

在索引節(jié)點(diǎn)里面的key進(jìn)行比對(duì)的時(shí)候羊赵,會(huì)按照id趟佃,職位名稱,出生日期的順序(由左到右)昧捷,比較的時(shí)候是逐一比對(duì)闲昭,一旦沒有匹配到指定字段,則不會(huì)繼續(xù)匹配靡挥,這也是我們常說的復(fù)合索引里面的最左前綴匹配原則序矩。

關(guān)于MySQL索引的基本介紹大致就只想到了這些,關(guān)于索引優(yōu)化方面后邊我會(huì)進(jìn)行系列的干貨輸出跋破,希望各位小伙伴喜歡簸淀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市毒返,隨后出現(xiàn)的幾起案子租幕,更是在濱河造成了極大的恐慌,老刑警劉巖拧簸,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劲绪,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)贾富,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門歉眷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颤枪,你說我怎么就攤上這事汗捡。” “怎么了畏纲?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵扇住,是天一觀的道長。 經(jīng)常有香客問我霍骄,道長台囱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任读整,我火速辦了婚禮,結(jié)果婚禮上咱娶,老公的妹妹穿的比我還像新娘米间。我一直安慰自己,他們只是感情好膘侮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布屈糊。 她就那樣靜靜地躺著,像睡著了一般琼了。 火紅的嫁衣襯著肌膚如雪逻锐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天雕薪,我揣著相機(jī)與錄音昧诱,去河邊找鬼。 笑死所袁,一個(gè)胖子當(dāng)著我的面吹牛盏档,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播燥爷,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜈亩,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了前翎?” 一聲冷哼從身側(cè)響起稚配,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎港华,沒想到半個(gè)月后道川,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年愤惰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苇经。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宦言,死狀恐怖扇单,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奠旺,我是刑警寧澤蜘澜,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站响疚,受9級(jí)特大地震影響鄙信,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忿晕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一装诡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧践盼,春花似錦鸦采、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肄程,卻和暖如春锣吼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蓝厌。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工玄叠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褂始。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓诸典,卻偏偏與公主長得像,于是被迫代替她去往敵國和親崎苗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狐粱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容