偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

再有人問(wèn)你為什么MySQL用B+樹(shù)做索引,就把這篇文章發(fā)給她

企業(yè)動(dòng)態(tài)
索引這個(gè)詞,相信大多數(shù)人已經(jīng)相當(dāng)熟悉了,很多人都知道MySQL的索引主要以B+樹(shù)為主,但是要問(wèn)到為什么用B+樹(shù),恐怕很少有人能把前因后果講述的很完整。本文就來(lái)從頭到尾介紹下數(shù)據(jù)庫(kù)的索引。

[[281358]]

索引這個(gè)詞,相信大多數(shù)人已經(jīng)相當(dāng)熟悉了,很多人都知道MySQL的索引主要以B+樹(shù)為主,但是要問(wèn)到為什么用B+樹(shù),恐怕很少有人能把前因后果講述的很完整。本文就來(lái)從頭到尾介紹下數(shù)據(jù)庫(kù)的索引。

索引是一種數(shù)據(jù)結(jié)構(gòu),用于幫助我們?cè)诖罅繑?shù)據(jù)中快速定位到我們想要查找的數(shù)據(jù)。 索引最形象的比喻就是圖書(shū)的目錄了。注意這里的大量,數(shù)據(jù)量大了索引才顯得有意義,如果我想要在[1,2,3,4]中找到4這個(gè)數(shù)據(jù),直接對(duì)全數(shù)據(jù)檢索也很快,沒(méi)有必要費(fèi)力氣建索引再去查找。索引在mysql數(shù)據(jù)庫(kù)中分三類(lèi):

B+樹(shù)索引、Hash索引、全文索引

我們今天要介紹的是工作開(kāi)發(fā)中最常接觸到innodb存儲(chǔ)引擎中的的B+樹(shù)索引。

要介紹B+樹(shù)索引,就不得不提二叉查找樹(shù),平衡二叉樹(shù)和B樹(shù)這三種數(shù)據(jù)結(jié)構(gòu)。B+樹(shù)就是從他們仨演化來(lái)的。

二叉查找樹(shù)

首先,讓我們先看一張圖:

從圖中可以看到,我們?yōu)閡ser表(用戶(hù)信息表)建立了一個(gè)二叉查找樹(shù)的索引。圖中的圓為二叉查找樹(shù)的節(jié)點(diǎn),節(jié)點(diǎn)中存儲(chǔ)了鍵(key)和數(shù)據(jù)(data)。

鍵對(duì)應(yīng)user表中的id,數(shù)據(jù)對(duì)應(yīng)user表中的行數(shù)據(jù)。二叉查找樹(shù)的特點(diǎn)就是任何節(jié)點(diǎn)的左子節(jié)點(diǎn)的鍵值都小于當(dāng)前節(jié)點(diǎn)的鍵值,右子節(jié)點(diǎn)的鍵值都大于當(dāng)前節(jié)點(diǎn)的鍵值。 頂端的節(jié)點(diǎn)我們稱(chēng)為根節(jié)點(diǎn),沒(méi)有子節(jié)點(diǎn)的節(jié)點(diǎn)我們稱(chēng)之為葉節(jié)點(diǎn)。

如果我們需要查找id=12的用戶(hù)信息,利用我們創(chuàng)建的二叉查找樹(shù)索引,查找流程如下:

  • 1. 將根節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn),把12與當(dāng)前節(jié)點(diǎn)的鍵值10比較,12大于10,接下來(lái)我們把當(dāng)前節(jié)點(diǎn)>的右子節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)。
  • 2. 繼續(xù)把12和當(dāng)前節(jié)點(diǎn)的鍵值13比較,發(fā)現(xiàn)12小于13,把當(dāng)前節(jié)點(diǎn)的左子節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)。
  • 3. 把12和當(dāng)前節(jié)點(diǎn)的鍵值12對(duì)比,12等于12,滿(mǎn)足條件,我們從當(dāng)前節(jié)點(diǎn)中取出data,即id=1>2,name=xm。

利用二叉查找樹(shù)我們只需要3次即可找到匹配的數(shù)據(jù)。如果在表中一條條的查找的話,我們需要6次才能找到。

平衡二叉樹(shù)

上面我們講解了利用二叉查找樹(shù)可以快速的找到數(shù)據(jù)。但是,如果上面的二叉查找樹(shù)是這樣的構(gòu)造:

 

這個(gè)時(shí)候可以看到我們的二叉查找樹(shù)變成了一個(gè)鏈表。

如果我們需要查找id=17的用戶(hù)信息,我們需要查找7次,也就相當(dāng)于全表掃描了。

導(dǎo)致這個(gè)現(xiàn)象的原因其實(shí)是二叉查找樹(shù)變得不平衡了,也就是高度太高了,從而導(dǎo)致查找效率的不穩(wěn)定。

為了解決這個(gè)問(wèn)題,我們需要保證二叉查找樹(shù)一直保持平衡,就需要用到平衡二叉樹(shù)了。

平衡二叉樹(shù)又稱(chēng)AVL樹(shù),在滿(mǎn)足二叉查找樹(shù)特性的基礎(chǔ)上,要求每個(gè)節(jié)點(diǎn)的左右子樹(shù)的高度不能超過(guò)1。

下面是平衡二叉樹(shù)和非平衡二叉樹(shù)的對(duì)比:

由平衡二叉樹(shù)的構(gòu)造我們可以發(fā)現(xiàn)第一張圖中的二叉樹(shù)其實(shí)就是一棵平衡二叉樹(shù)。

平衡二叉樹(shù)保證了樹(shù)的構(gòu)造是平衡的,當(dāng)我們插入或刪除數(shù)據(jù)導(dǎo)致不滿(mǎn)足平衡二叉樹(shù)不平衡時(shí),平衡二叉樹(shù)會(huì)進(jìn)行調(diào)整樹(shù)上的節(jié)點(diǎn)來(lái)保持平衡。具體的調(diào)整方式這里就不介紹了。

平衡二叉樹(shù)相比于二叉查找樹(shù)來(lái)說(shuō),查找效率更穩(wěn)定,總體的查找速度也更快。

B樹(shù)

因?yàn)閮?nèi)存的易失性。一般情況下,我們都會(huì)選擇將user表中的數(shù)據(jù)和索引存儲(chǔ)在磁盤(pán)這種外圍設(shè)備中。

但是和內(nèi)存相比,從磁盤(pán)中讀取數(shù)據(jù)的速度會(huì)慢上百倍千倍甚至萬(wàn)倍,所以,我們應(yīng)當(dāng)盡量減少?gòu)拇疟P(pán)中讀取數(shù)據(jù)的次數(shù)。 另外,從磁盤(pán)中讀取數(shù)據(jù)時(shí),都是按照磁盤(pán)塊來(lái)讀取的,并不是一條一條的讀。

如果我們能把盡量多的數(shù)據(jù)放進(jìn)磁盤(pán)塊中,那一次磁盤(pán)讀取操作就會(huì)讀取更多數(shù)據(jù),那我們查找數(shù)據(jù)的時(shí)間也會(huì)大幅度降低。

如果我們用樹(shù)這種數(shù)據(jù)結(jié)構(gòu)作為索引的數(shù)據(jù)結(jié)構(gòu),那我們每查找一次數(shù)據(jù)就需要從磁盤(pán)中讀取一個(gè)節(jié)點(diǎn),也就是我們說(shuō)的一個(gè)磁盤(pán)塊,我們都知道平衡二叉樹(shù)可是每個(gè)節(jié)點(diǎn)只存儲(chǔ)一個(gè)鍵值和數(shù)據(jù)的。

那說(shuō)明什么?

說(shuō)明每個(gè)磁盤(pán)塊僅僅存儲(chǔ)一個(gè)鍵值和數(shù)據(jù)!

那如果我們要存儲(chǔ)海量的數(shù)據(jù)呢?

可以想象到二叉樹(shù)的節(jié)點(diǎn)將會(huì)非常多,高度也會(huì)及其高,我們查找數(shù)據(jù)時(shí)也會(huì)進(jìn)行很多次磁盤(pán)IO,我們查找數(shù)據(jù)的效率將會(huì)極低!

為了解決平衡二叉樹(shù)的這個(gè)弊端,我們應(yīng)該尋找一種單個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)鍵值和數(shù)據(jù)的平衡樹(shù)。也就是我們接下來(lái)要說(shuō)的B樹(shù)。

B樹(shù)(Balance Tree)即為平衡樹(shù)的意思,下圖即是一顆B樹(shù)。

圖中的p節(jié)點(diǎn)為指向子節(jié)點(diǎn)的指針,二叉查找樹(shù)和平衡二叉樹(shù)其實(shí)也有,因?yàn)閳D的美觀性,被省略了。- 圖中的每個(gè)節(jié)點(diǎn)稱(chēng)為頁(yè),頁(yè)就是我們上面說(shuō)的磁盤(pán)塊,在mysql中數(shù)據(jù)讀取的基本單位都是頁(yè),所以我們這里叫做頁(yè)更符合mysql中索引的底層數(shù)據(jù)結(jié)構(gòu)。

從上圖可以看出,B樹(shù)相對(duì)于平衡二叉樹(shù),每個(gè)節(jié)點(diǎn)存儲(chǔ)了更多的鍵值(key)和數(shù)據(jù)(data),并且每個(gè)節(jié)點(diǎn)擁有更多的子節(jié)點(diǎn),子節(jié)點(diǎn)的個(gè)數(shù)一般稱(chēng)為階,上述圖中的B樹(shù)為3階B樹(shù),高度也會(huì)很低。 基于這個(gè)特性,B樹(shù)查找數(shù)據(jù)讀取磁盤(pán)的次數(shù)將會(huì)很少,數(shù)據(jù)的查找效率也會(huì)比平衡二叉樹(shù)高很多。

假如我們要查找id=28的用戶(hù)信息,那么我們?cè)谏蠄DB樹(shù)中查找的流程如下:

1. 先找到根節(jié)點(diǎn)也就是頁(yè)1,判斷28在鍵值17和35之間,我們那么我們根據(jù)頁(yè)1中的指針p2找到頁(yè)3。

2. 將28和頁(yè)3中的鍵值相比較,28在26和30之間,我們根據(jù)頁(yè)3中的指針p2找到頁(yè)8。

3. 將28和頁(yè)8中的鍵值相比較,發(fā)現(xiàn)有匹配的鍵值28,鍵值28對(duì)應(yīng)的用戶(hù)信息為(28,bv)。

B+樹(shù)

B+樹(shù)是對(duì)B樹(shù)的進(jìn)一步優(yōu)化。讓我們先來(lái)看下B+樹(shù)的結(jié)構(gòu)圖:

根據(jù)上圖我們來(lái)看下B+樹(shù)和B樹(shù)有什么不同。 

1. B+樹(shù)非葉子節(jié)點(diǎn)上是不存儲(chǔ)數(shù)據(jù)的,僅存儲(chǔ)鍵值,而B(niǎo)樹(shù)節(jié)點(diǎn)中不僅存儲(chǔ)鍵值,也會(huì)存儲(chǔ)數(shù)據(jù)。之所以這么做是因?yàn)樵跀?shù)據(jù)庫(kù)中頁(yè)的大小是固定的,innodb中頁(yè)的默認(rèn)大小是16KB。如果不存儲(chǔ)數(shù)據(jù),那么就會(huì)存儲(chǔ)更多的鍵值,相應(yīng)的樹(shù)的階數(shù)(節(jié)點(diǎn)的子節(jié)點(diǎn)樹(shù))就會(huì)更大,樹(shù)就會(huì)更矮更胖,如此一來(lái)我們查找數(shù)據(jù)進(jìn)行磁盤(pán)的IO次數(shù)有會(huì)再次減少,數(shù)據(jù)查詢(xún)的效率也會(huì)更快。另外,B+樹(shù)的階數(shù)是等于鍵值的數(shù)量的,如果我們的B+樹(shù)一個(gè)節(jié)點(diǎn)可以存儲(chǔ)1000個(gè)鍵值,那么3層B+樹(shù)可以存儲(chǔ)1000×1000×1000=10億個(gè)數(shù)據(jù)。一般根節(jié)點(diǎn)是常駐內(nèi)存的,所以一般我們查找10億數(shù)據(jù),只需要2次磁盤(pán)IO。

2. 因?yàn)锽+樹(shù)索引的所有數(shù)據(jù)均存儲(chǔ)在葉子節(jié)點(diǎn),而且數(shù)據(jù)是按照順序排列的。那么B+樹(shù)使得范圍查找,排序查找,分組查找以及去重查找變得異常簡(jiǎn)單。而B(niǎo)樹(shù)因?yàn)閿?shù)據(jù)分散在各個(gè)節(jié)點(diǎn),要實(shí)現(xiàn)這一點(diǎn)是很不容易的。 有心的讀者可能還發(fā)現(xiàn)上圖B+樹(shù)中各個(gè)頁(yè)之間是通過(guò)雙向鏈表連接的,葉子節(jié)點(diǎn)中的數(shù)據(jù)是通過(guò)單向鏈表連接的。其實(shí)上面的B樹(shù)我們也可以對(duì)各個(gè)節(jié)點(diǎn)加上鏈表。其實(shí)這些不是它們之前的區(qū)別,是因?yàn)樵趍ysql的innodb存儲(chǔ)引擎中,索引就是這樣存儲(chǔ)的。也就是說(shuō)上圖中的B+樹(shù)索引就是innodb中B+樹(shù)索引真正的實(shí)現(xiàn)方式,準(zhǔn)確的說(shuō)應(yīng)該是聚集索引(聚集索引和非聚集索引下面會(huì)講到)。通過(guò)上圖可以看到,在innodb中,我們通過(guò)數(shù)據(jù)頁(yè)之間通過(guò)雙向鏈表連接以及葉子節(jié)點(diǎn)中數(shù)據(jù)之間通過(guò)單向鏈表連接的方式可以找到表中所有的數(shù)據(jù)。

MyISAM中的B+樹(shù)索引實(shí)現(xiàn)與innodb中的略有不同。在MyISAM中,B+樹(shù)索引的葉子節(jié)點(diǎn)并不存儲(chǔ)數(shù)據(jù),而是存儲(chǔ)數(shù)據(jù)的文件地址。

聚集索引 VS 非聚集索引

在上節(jié)介紹B+樹(shù)索引的時(shí)候,我們提到了圖中的索引其實(shí)是聚集索引的實(shí)現(xiàn)方式。那什么是聚集索引呢?在MySQL中,B+樹(shù)索引按照存儲(chǔ)方式的不同分為聚集索引和非聚集索引。這里我們著重介紹innodb中的聚集索引和非聚集索引。1. 聚集索引(聚簇索引):以innodb作為存儲(chǔ)引擎的表,表中的數(shù)據(jù)都會(huì)有一個(gè)主鍵,即使你不創(chuàng)建主鍵,系統(tǒng)也會(huì)幫你創(chuàng)建一個(gè)隱式的主鍵。這是因?yàn)閕nnodb是把數(shù)據(jù)存放在B+樹(shù)中的,而B(niǎo)+樹(shù)的鍵值就是主鍵,在B+樹(shù)的葉子節(jié)點(diǎn)中,存儲(chǔ)了表中所有的數(shù)據(jù)。這種以主鍵作為B+樹(shù)索引的鍵值而構(gòu)建的B+樹(shù)索引,我們稱(chēng)之為聚集索引。 2. 非聚集索引(非聚簇索引):以主鍵以外的列值作為鍵值構(gòu)建的B+樹(shù)索引,我們稱(chēng)之為非聚集索引。非聚集索引與聚集索引的區(qū)別在于非聚集索引的葉子節(jié)點(diǎn)不存儲(chǔ)表中的數(shù)據(jù),而是存儲(chǔ)該列對(duì)應(yīng)的主鍵,想要查找數(shù)據(jù)我們還需要根據(jù)主鍵再去聚集索引中進(jìn)行查找,這個(gè)再根據(jù)聚集索引查找數(shù)據(jù)的過(guò)程,我們稱(chēng)為回表。

明白了聚集索引和非聚集索引的定義,我們應(yīng)該明白這樣一句話:數(shù)據(jù)即索引,索引即數(shù)據(jù)。

利用聚集索引和非聚集索引查找數(shù)據(jù)

前面我們講解B+樹(shù)索引的時(shí)候并沒(méi)有去說(shuō)怎么在B+樹(shù)中進(jìn)行數(shù)據(jù)的查找,主要就是因?yàn)檫€沒(méi)有引出聚集索引和非聚集索引的概念。下面我們通過(guò)講解如何通過(guò)聚集索引以及非聚集索引查找數(shù)據(jù)表中數(shù)據(jù)的方式介紹一下B+樹(shù)索引查找數(shù)據(jù)方法。

利用聚集索引查找數(shù)據(jù)

還是這張B+樹(shù)索引圖,現(xiàn)在我們應(yīng)該知道這就是聚集索引,表中的數(shù)據(jù)存儲(chǔ)在其中?,F(xiàn)在假設(shè)我們要查找id>=18并且id<40的用戶(hù)數(shù)據(jù)。對(duì)應(yīng)的sql語(yǔ)句為select * from user where id>=18 and id <40,其中id為主鍵。具體的查找過(guò)程如下: 

1. 一般根節(jié)點(diǎn)都是常駐內(nèi)存的,也就是說(shuō)頁(yè)1已經(jīng)在內(nèi)存中了,此時(shí)不需要到磁盤(pán)中讀取數(shù)據(jù),直接從內(nèi)存中讀取即可。

從內(nèi)存中讀取到頁(yè)1,要查找這個(gè)id>=18 and id <40或者范圍值,我們首先需要找到id=18的鍵值。

從頁(yè)1中我們可以找到鍵值18,此時(shí)我們需要根據(jù)指針p2,定位到頁(yè)3。

2. 要從頁(yè)3中查找數(shù)據(jù),我們就需要拿著p2指針去磁盤(pán)中進(jìn)行讀取頁(yè)3。

從磁盤(pán)中讀取頁(yè)3后將頁(yè)3放入內(nèi)存中,然后進(jìn)行查找,我們可以找到鍵值18,然后再拿到頁(yè)3中的指針p1,定位到頁(yè)8。

3. 同樣的頁(yè)8頁(yè)不在內(nèi)存中,我們需要再去磁盤(pán)中將頁(yè)8讀取到內(nèi)存中。

將頁(yè)8讀取到內(nèi)存中后。

因?yàn)轫?yè)中的數(shù)據(jù)是鏈表進(jìn)行連接的,而且鍵值是按照順序存放的,此時(shí)可以根據(jù)二分查找法定位到鍵值18。

此時(shí)因?yàn)橐呀?jīng)到數(shù)據(jù)頁(yè)了,此時(shí)我們已經(jīng)找到一條滿(mǎn)足條件的數(shù)據(jù)了,就是鍵值18對(duì)應(yīng)的數(shù)據(jù)。

因?yàn)槭欠秶檎?,而且此時(shí)所有的數(shù)據(jù)又都存在葉子節(jié)點(diǎn),并且是有序排列的,那么我們就可以對(duì)頁(yè)8中的鍵值依次進(jìn)行遍歷查找并匹配滿(mǎn)足條件的數(shù)據(jù)。

我們可以一直找到鍵值為22的數(shù)據(jù),然后頁(yè)8中就沒(méi)有數(shù)據(jù)了,此時(shí)我們需要拿著頁(yè)8中的p指針去讀取頁(yè)9中的數(shù)據(jù)。

4. 因?yàn)轫?yè)9不在內(nèi)存中,就又會(huì)加載頁(yè)9到內(nèi)存中,并通過(guò)和頁(yè)8中一樣的方式進(jìn)行數(shù)據(jù)的查找,直到將頁(yè)12加載到內(nèi)存中,發(fā)現(xiàn)41大于40,此時(shí)不滿(mǎn)足條件。

那么查找到此終止。

最終我們找到滿(mǎn)足條件的所有數(shù)據(jù)為:

(18,kl),(19,kl),(22,hj),(24,io),(25,vg),(29,jk),(31,jk),(33,rt),(34,ty),(35,yu),(37,rt),(39,rt)。

總共12條記錄。

下面看下具體的查找流程圖:

利用非聚集索引查找數(shù)據(jù)

讀者看到這張圖的時(shí)候可能會(huì)蒙,這是啥東西啊?怎么都是數(shù)字。如果有這種感覺(jué),請(qǐng)仔細(xì)看下圖中紅字的解釋。什么?還看不懂?那我再來(lái)解釋下吧。首先,這個(gè)非聚集索引表示的是用戶(hù)幸運(yùn)數(shù)字的索引(為什么是幸運(yùn)數(shù)字?一時(shí)興起想起來(lái)的:-)),此時(shí)表結(jié)構(gòu)是這樣的。idnameluckyNum 

在葉子節(jié)點(diǎn)中,不在存儲(chǔ)所有的數(shù)據(jù)了,存儲(chǔ)的是鍵值和主鍵。對(duì)于葉子節(jié)點(diǎn)中的x-y,比如1-1。左邊的1表示的是索引的鍵值,右邊的1表示的是主鍵值。如果我們要找到幸運(yùn)數(shù)字為33的用戶(hù)信息,對(duì)應(yīng)的sql語(yǔ)句為select * from user where luckNum=33。查找的流程跟聚集索引一樣,這里就不詳細(xì)介紹了。我們最終會(huì)找到主鍵值47,找到主鍵后我們需要再到聚集索引中查找具體對(duì)應(yīng)的數(shù)據(jù)信息,此時(shí)又回到了聚集索引的查找流程。 下面看下具體的查找流程圖:

在MyISAM中,聚集索引和非聚集索引的葉子節(jié)點(diǎn)都會(huì)存儲(chǔ)數(shù)據(jù)的文件地址。

總結(jié)

本篇文從二叉查找樹(shù),詳細(xì)說(shuō)明了為什么mysql用B+樹(shù)作為數(shù)據(jù)的索引,以及在innodb中數(shù)據(jù)庫(kù)如何通過(guò)B+樹(shù)索引來(lái)存儲(chǔ)數(shù)據(jù)以及查找數(shù)據(jù)。我們一定要記住這就話:數(shù)據(jù)即索引,索引即數(shù)據(jù)。參考資料:

《Mysql必知必會(huì)》

《MySQL技術(shù)內(nèi)幕InnoDB存儲(chǔ)引擎第2版》

https://www.cnblogs.com/vianzhang/p/7922426.html

http://blog.codinglabs.org/articles/theory-of-mysql-index.html

【本文是51CTO專(zhuān)欄作者Hollis的原創(chuàng)文章,作者微信公眾號(hào)Hollis(ID:hollischuang)】 

戳這里,看該作者更多好文

 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2019-11-04 15:00:50

MySQL索引B+樹(shù)

2022-09-26 10:09:08

MVCC控制并發(fā)

2018-08-07 16:01:32

synchronizevolatilefinal

2021-02-22 13:32:19

MySQLSQL索引

2020-12-11 08:23:06

JavaMemory Mode內(nèi)存模型

2018-07-03 14:54:25

Java內(nèi)存模型

2018-12-07 09:31:52

分布式鎖服務(wù)框架分布式系統(tǒng)

2021-05-31 09:42:48

MySQL隔離級(jí)別

2022-11-08 09:33:36

訂單系統(tǒng)電商

2019-09-19 14:03:32

B樹(shù)節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)

2022-05-23 09:41:27

分庫(kù)分表數(shù)據(jù)庫(kù)算法

2021-03-08 10:25:37

MySQL數(shù)據(jù)庫(kù)索引

2021-03-08 12:47:42

MySQL查詢(xún)數(shù)據(jù)

2022-10-21 16:39:56

JDK優(yōu)化

2022-03-28 08:24:52

MySQL聚簇索引非聚簇索引

2019-09-24 09:33:53

MySQLB+樹(shù)InnoDB

2023-12-11 08:32:58

數(shù)據(jù)庫(kù)DruidDBA

2024-05-22 09:01:53

InnoDBB+索引

2020-04-20 13:11:21

HashMap底層存儲(chǔ)

2019-03-14 09:51:50

MySQL存儲(chǔ)邏輯架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)