午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

一步步分析為什么B+樹適合作為索引的結(jié)構(gòu) 以及索引原理

 悅光陰 2022-03-25

 

https://www.cnblogs.com/aspirant/p/9214485.html

一步步分析為什么B+樹適合作為索引的結(jié)構(gòu) 以及索引原理 

 

mysql的B+樹索引 查找使用了二分查找,redis 跳表也使用了二分查找法,kafka查詢消息日志也使用了二分查找法,二分查找法時(shí)間復(fù)雜度O(logn);

參考:redis的索引底層的 跳表原理 實(shí)現(xiàn) 聊聊Mysql索引和redis跳表 ---redis的跳表原理 時(shí)間復(fù)雜度O(logn)(阿里)

參考:kafka如何實(shí)現(xiàn)高并發(fā)存儲(chǔ)-如何找到一條需要消費(fèi)的數(shù)據(jù)(阿里)

參考:二分查找法:各種排序算法的時(shí)間復(fù)雜度和空間復(fù)雜度(阿里)

在MySQL中,主要有四種類型的索引,分別為:B-Tree索引,Hash索引,F(xiàn)ulltext索引(MyISAM 表)和R-Tree索引,本文講的是B-Tree索引。

后面的索引原理一定要看,太重要了,阿里兩個(gè)人都問這個(gè)mysql的索引原理

mysql使用了 B+索引:

B樹:有序數(shù)組+平衡多叉樹; 
B+樹:有序數(shù)組鏈表+平衡多叉樹;

一、Mysql索引主要有兩種結(jié)構(gòu):B+Tree索引和Hash索引 

(a) Inodb存儲(chǔ)引擎 默認(rèn)是 B+Tree索引

(b) MyISAM 存儲(chǔ)引擎 默認(rèn)是Fulltext索引;

(c)Memory 存儲(chǔ)引擎 默認(rèn) Hash索引;

Hash索引

mysql中,只有Memory(Memory表只存在內(nèi)存中,斷電會(huì)消失,適用于臨時(shí)表)存儲(chǔ)引擎顯示支持Hash索引,是Memory表的默認(rèn)索引類型,盡管Memory表也可以使用B+Tree索引。Hash索引把數(shù)據(jù)以hash形式組織起來,因此當(dāng)查找某一條記錄的時(shí)候,速度非常快。但是因?yàn)閔ash結(jié)構(gòu),每個(gè)鍵只對(duì)應(yīng)一個(gè)值,而且是散列的方式分布。所以它并不支持范圍查找和排序等功能。

 

B+Tree索引

B+Tree是mysql使用最頻繁的一個(gè)索引數(shù)據(jù)結(jié)構(gòu),是Inodb和Myisam存儲(chǔ)引擎模式的索引類型。相對(duì)Hash索引,B+Tree在查找單條記錄的速度比不上Hash索引,但是因?yàn)楦m合排序等操作,所以它更受歡迎。畢竟不可能只對(duì)數(shù)據(jù)庫(kù)進(jìn)行單條記錄的操作。

帶順序訪問指針的B+Tree

B+Tree所有索引數(shù)據(jù)都在葉子節(jié)點(diǎn)上,并且增加了順序訪問指針,每個(gè)葉子節(jié)點(diǎn)都有指向相鄰葉子節(jié)點(diǎn)的指針。

這樣做是為了提高區(qū)間效率,例如查詢key為從18到49的所有數(shù)據(jù)記錄,當(dāng)找到18后,只要順著節(jié)點(diǎn)和指針順序遍歷就可以以此向訪問到所有數(shù)據(jù)節(jié)點(diǎn),極大提高了區(qū)間查詢效率。

大大減少磁盤I/O讀取

數(shù)據(jù)庫(kù)系統(tǒng)的設(shè)計(jì)者巧妙利用了磁盤預(yù)讀原理,將一個(gè)節(jié)點(diǎn)的大小設(shè)為等于一個(gè)頁,這樣每個(gè)節(jié)點(diǎn)需要一次I/O就可以完全載入。 

 

什么是索引

索引(Index)是幫助數(shù)據(jù)庫(kù)高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。索引是在基于數(shù)據(jù)庫(kù)表創(chuàng)建的,它包含一個(gè)表中某些列的值以及記錄對(duì)應(yīng)的地址,并且把這些值存儲(chǔ)在一個(gè)數(shù)據(jù)結(jié)構(gòu)中。最常見的就是使用哈希表、B+樹作為索引。

一般的應(yīng)用系統(tǒng),讀寫比例在10:1左右,而且插入操作和一般的更新操作很少出現(xiàn)性能問題,在生產(chǎn)環(huán)境中,我們遇到最多的,也是最容易出問題的,還是一些復(fù)雜的查詢操作,因此對(duì)查詢語句的優(yōu)化顯然是重中之重。說起加速查詢,就不得不提到索引了。

 

為什么要使用索引

我們知道,數(shù)據(jù)庫(kù)查詢是數(shù)據(jù)庫(kù)最主要的功能之一。而查詢速度當(dāng)然是越快越好。而當(dāng)數(shù)據(jù)量越來越大的時(shí)候,查詢花費(fèi)的時(shí)間會(huì)隨之增長(zhǎng)。而索引,可以加速數(shù)據(jù)的查詢。因?yàn)樗饕怯行蚺帕械摹?/p>

舉個(gè)例子來說,假設(shè)我們有一個(gè)數(shù)據(jù)庫(kù)表Employee,這個(gè)表分別有三個(gè)字段:name,age,address。假設(shè)表中有1000條記錄。

假如沒有使用索引,當(dāng)我們查詢名為“Jesus”的雇員的時(shí)候,即調(diào)用:

select name,age,address from Employee where name = 'Jesus';

此時(shí)數(shù)據(jù)庫(kù)不得不在Employee表中對(duì)這1000條記錄一條一條的進(jìn)行判斷name字段是否為“Jesus”。這也就是所謂的全表掃描。

而當(dāng)我們?cè)贓mployee表上的name字段上創(chuàng)建索引時(shí),當(dāng)我們查詢名為“Jesus”的雇員時(shí),會(huì)通過索引查找去查詢名為“Jesus”的雇員,因?yàn)樵撍饕呀?jīng)按照字母順序排列,因此要查找名為“Jesus”的記錄時(shí)會(huì)快很多,因?yàn)槊质鬃帜笧椤癑”的雇員都是排列在一起的。通過該索引,能獲取到表中對(duì)應(yīng)的記錄。

舉例說明使用索引的好處

假設(shè)索引(索引是一種數(shù)據(jù)結(jié)構(gòu))是鏈表結(jié)構(gòu)。每個(gè)節(jié)點(diǎn)存儲(chǔ)的是關(guān)鍵字字段(這個(gè)例子中對(duì)應(yīng)的是name屬性)以及該關(guān)鍵字字段在數(shù)據(jù)庫(kù)表的對(duì)應(yīng)的記錄的地址。而這些節(jié)點(diǎn)是根據(jù)name屬性排序的(即根據(jù)字母順序排序)。因此,當(dāng)我們執(zhí)行上面說的查找名為“Jesus”的sql語句時(shí),數(shù)據(jù)庫(kù)會(huì)通過該索引來查詢,因?yàn)樵撴湵硎怯行蚺帕械?,在我們找到第一個(gè)name屬性為“Jesus”的節(jié)點(diǎn)后,繼續(xù)往后找,當(dāng)遇到name屬性不為“Jesus”的節(jié)點(diǎn)時(shí),就無需再往后查找了,因?yàn)楣?jié)點(diǎn)是根據(jù)name屬性有序排列的啊。假設(shè)第一個(gè)name=“Jesus”的節(jié)點(diǎn)是第499個(gè)節(jié)點(diǎn),最后一個(gè)name=“Jesus”的節(jié)點(diǎn)是第500個(gè)節(jié)點(diǎn),那么只需要遍歷501個(gè)節(jié)點(diǎn)就可以了。當(dāng)發(fā)現(xiàn)第501個(gè)節(jié)點(diǎn)的name字段不為“Jesus”,后面的499個(gè)節(jié)點(diǎn)也就無需遍歷了。通過索引,我們就找到了name為“Jesus”的節(jié)點(diǎn),而通過該節(jié)點(diǎn)的另一個(gè)屬性(關(guān)鍵字字段在數(shù)據(jù)庫(kù)表的對(duì)應(yīng)的記錄的地址),我們就能獲取到Employee表中滿足條件name=“Jesus”的記錄了。

通過使用索引,查詢判斷的次數(shù)就從1000次縮小到了501次了。起到了加速了查詢效率。但實(shí)際上數(shù)據(jù)庫(kù)中索引的結(jié)構(gòu),并不是鏈表結(jié)構(gòu)。

數(shù)據(jù)庫(kù)中使用什么數(shù)據(jù)結(jié)構(gòu)作為索引

數(shù)據(jù)庫(kù)中實(shí)際使用的索引并不會(huì)是鏈表結(jié)構(gòu),因?yàn)樾侍土恕?nbsp;
我們知道鏈表的查詢效率是O(n)。就像上面的例子,遍歷了501次才找到第一條符合條件的記錄,這是很低效的。而我們知道,數(shù)組+二分查找的效率是O(lgn),但是數(shù)組的插入元素以及刪除元素的效率很低,因此使用數(shù)組做為索引結(jié)構(gòu)并不合適。

另外,在選擇數(shù)據(jù)庫(kù)索引的結(jié)構(gòu)的時(shí)候,要考慮到另一個(gè)問題。索引是存在于磁盤中,當(dāng)索引非常大的時(shí)候,達(dá)到幾個(gè)G的時(shí)候,無法一次加載到內(nèi)存中。

考慮到上面兩個(gè)因素,數(shù)據(jù)庫(kù)中索引使用的是樹形結(jié)構(gòu)。

各種樹的名字

有這么幾種樹:

B-Tree
B+-Tree
B*-Tree

首先要明白三種樹名中的“-”起到的是分隔的作用,并不是“減”的意思。 

因此正確的翻譯應(yīng)該是B樹,B+樹,B*樹。而不是B-樹,B+樹,B*樹。因此,當(dāng)你聽到別人說“B減樹”的時(shí)候,要明白它指的是B-Tree。即B樹和B-樹是同一種樹。

為什么要強(qiáng)調(diào)上面這一點(diǎn)呢,因?yàn)橛械牟┪闹袑懙氖牵築樹是二叉樹,B-樹是多路搜索樹。

然而B樹和B-樹都是指B-Tree。引用維基百科上的話:

B-tree 
Not to be confused with Binary tree.

 

也就是說,B-Tree并不是Binart tree。B-Tree的中文名是平衡多路搜索樹。 
(B樹的相關(guān)介紹在下面)

平衡二叉樹

樹形結(jié)構(gòu)是計(jì)算機(jī)系統(tǒng)里最重要的數(shù)據(jù)結(jié)構(gòu)。

我們知道,二叉樹的查找的時(shí)間復(fù)雜度是O(log2N),其查找效率與深度有關(guān),而普通的二叉樹可能由于內(nèi)部節(jié)點(diǎn)排列問題退化成鏈表,這樣查找效率就會(huì)很低。因此平衡二叉樹是更好的選擇,因?yàn)樗3制胶?,即通過旋轉(zhuǎn)調(diào)整結(jié)構(gòu)保持最小的深度。其查找的時(shí)間復(fù)雜度也是O(log2N)。

但實(shí)際上,數(shù)據(jù)庫(kù)中索引的結(jié)構(gòu)也并非AVL樹或更優(yōu)秀的紅黑樹,盡管它的查詢的時(shí)間復(fù)雜度很低。

為什么平衡二叉樹也不適合作為索引

之前說了平衡樹的查找時(shí)間復(fù)雜度是O(log2N),已經(jīng)很不錯(cuò)了,但還是不適合作為索引結(jié)構(gòu)。那么肯定是有一種更適合作為索引的數(shù)據(jù)結(jié)構(gòu)。那么這個(gè)更適合作為索引的數(shù)據(jù)結(jié)構(gòu),難道是查找的時(shí)間復(fù)雜度更低嗎?并不是。這種作為索引的數(shù)據(jù)結(jié)構(gòu)的查找的時(shí)間復(fù)雜度也近似O(log2N)。

那為什么平衡二叉樹不適合作為索引呢?

索引是存在于索引文件中,是存在于磁盤中的。因?yàn)樗饕ǔJ呛艽蟮?,因此無法一次將全部索引加載到內(nèi)存當(dāng)中,因此每次只能從磁盤中讀取一個(gè)磁盤頁的數(shù)據(jù)到內(nèi)存中。而這個(gè)磁盤的讀取的速度較內(nèi)存中的讀取速度而言是差了好幾個(gè)級(jí)別。

注意,我們說的平衡二叉樹結(jié)構(gòu),指的是邏輯結(jié)構(gòu)上的平衡二叉樹,其物理實(shí)現(xiàn)是數(shù)組。然后由于在邏輯結(jié)構(gòu)上相近的節(jié)點(diǎn)在物理結(jié)構(gòu)上可能會(huì)差很遠(yuǎn)。因此,每次讀取的磁盤頁的數(shù)據(jù)中有許多是用不上的。因此,查找過程中要進(jìn)行許多次的磁盤讀取操作。

而適合作為索引的結(jié)構(gòu)應(yīng)該是盡可能少的執(zhí)行磁盤IO操作,因?yàn)閳?zhí)行磁盤IO操作非常的耗時(shí)。因此,平衡二叉樹并不適合作為索引結(jié)構(gòu)。

B-Tree適合作為索引

平衡二叉樹不適合作為索引。那么什么才適合作為索引——B樹。

平衡二叉樹沒能充分利用磁盤預(yù)讀功能,而B樹是為了充分利用磁盤預(yù)讀功能來而創(chuàng)建的一種數(shù)據(jù)結(jié)構(gòu),也就是說B樹就是為了作為索引才被發(fā)明出來的的。

來看看關(guān)于“局部性原理與磁盤預(yù)讀”的知識(shí): 

復(fù)制代碼
局部性原理與磁盤預(yù)讀:

由于存儲(chǔ)介質(zhì)的特性,磁盤本身存取就比主存慢很多,再加上機(jī)械運(yùn)動(dòng)耗費(fèi),磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達(dá)到這個(gè)目的,磁盤往往不是嚴(yán)格按需讀取,而是每次都會(huì)預(yù)讀,即使只需要一個(gè)字節(jié),磁盤也會(huì)從這個(gè)位置開始,順序向后讀取一定長(zhǎng)度的數(shù)據(jù)放入內(nèi)存。這樣做的理論依據(jù)是計(jì)算機(jī)科學(xué)中著名的局部性原理: 
當(dāng)一個(gè)數(shù)據(jù)被用到時(shí),其附近的數(shù)據(jù)也通常會(huì)馬上被使用。 
程序運(yùn)行期間所需要的數(shù)據(jù)通常比較集中。 
由于磁盤順序讀取的效率很高(不需要尋道時(shí)間,只需很少的旋轉(zhuǎn)時(shí)間),因此對(duì)于具有局部性的程序來說,預(yù)讀可以提高I/O效率。
復(fù)制代碼

 

搞清楚上面的意思。磁盤預(yù)讀是具體實(shí)現(xiàn),其理論依據(jù)是局部性原理。

為什么說紅黑樹沒能充分利用磁盤預(yù)讀功能,引用一篇博文的一段話: 

紅黑樹這種結(jié)構(gòu),h明顯要深的多。由于邏輯上很近的節(jié)點(diǎn)(父子)物理上可能很遠(yuǎn),無法利用局部性,所以紅黑樹的I/O漸進(jìn)復(fù)雜度也為O(h),效率明顯比B-Tree差很多。

 

也就是說,使用紅黑樹(平衡二叉樹)結(jié)構(gòu)的話,每次磁盤預(yù)讀中的很多數(shù)據(jù)是用不上的數(shù)據(jù)。因此,它沒能利用好磁盤預(yù)讀的提供的數(shù)據(jù)。然后又由于深度大(較B樹而言),所以進(jìn)行的磁盤IO操作更多。

B樹的每個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)關(guān)鍵字,它將節(jié)點(diǎn)大小設(shè)置為磁盤頁的大小,充分利用了磁盤預(yù)讀的功能。每次讀取磁盤頁時(shí)就會(huì)讀取一整個(gè)節(jié)點(diǎn)。也正因每個(gè)節(jié)點(diǎn)存儲(chǔ)著非常多個(gè)關(guān)鍵字,樹的深度就會(huì)非常的小。進(jìn)而要執(zhí)行的磁盤讀取操作次數(shù)就會(huì)非常少,更多的是在內(nèi)存中對(duì)讀取進(jìn)來的數(shù)據(jù)進(jìn)行查找。

B樹的查詢,主要發(fā)生在內(nèi)存中,而平衡二叉樹的查詢,則是發(fā)生在磁盤讀取中。因此,雖然B樹查詢查詢的次數(shù)不比平衡二叉樹的次數(shù)少,但是相比起磁盤IO速度,內(nèi)存中比較的耗時(shí)就可以忽略不計(jì)了。因此,B樹更適合作為索引。

比B樹更適合作為索引的結(jié)構(gòu)——B+樹

比B樹更適合作為索引的結(jié)構(gòu)是B+樹。MySQL中也是使用B+樹作為索引。它是B樹的變種,因此是基于B樹來改進(jìn)的。為什么B+樹會(huì)比B樹更加優(yōu)秀呢?

B樹:有序數(shù)組+平衡多叉樹; 
B+樹:有序數(shù)組鏈表+平衡多叉樹;

B+樹的關(guān)鍵字全部存放在葉子節(jié)點(diǎn)中,非葉子節(jié)點(diǎn)用來做索引,而葉子節(jié)點(diǎn)中有一個(gè)指針指向一下個(gè)葉子節(jié)點(diǎn)。做這個(gè)優(yōu)化的目的是為了提高區(qū)間訪問的性能。而正是這個(gè)特性決定了B+樹更適合用來存儲(chǔ)外部數(shù)據(jù)。

引用一段話: 

復(fù)制代碼
走進(jìn)搜索引擎的作者梁斌老師針對(duì)B樹、B+樹給出了他的意見(為了真實(shí)性,特引用其原話,未作任何改動(dòng)): “B+樹還有一個(gè)最大的好處,方便掃庫(kù),B樹必須用中序遍歷的方法按序掃庫(kù),而B+樹直接從葉子結(jié)點(diǎn)挨個(gè)掃一遍就完了,B+樹支持range-query非常方便,而B樹不支持。這是數(shù)據(jù)庫(kù)選用B+樹的最主要原因。 
比如要查 5-10之間的,B+樹一把到5這個(gè)標(biāo)記,再一把到10,然后串起來就行了,B樹就非常麻煩。B樹的好處,就是成功查詢特別有利,因?yàn)闃涞母叨瓤傮w要比B+樹矮。不成功的情況下,B樹也比B+樹稍稍占一點(diǎn)點(diǎn)便宜。 
B樹比如你的例子中查,17的話,一把就得到結(jié)果了, 
有很多基于頻率的搜索是選用B樹,越頻繁query的結(jié)點(diǎn)越往根上走,前提是需要對(duì)query做統(tǒng)計(jì),而且要對(duì)key做一些變化。 
另外B樹也好B+樹也好,根或者上面幾層因?yàn)楸环磸?fù)query,所以這幾塊基本都在內(nèi)存中,不會(huì)出現(xiàn)讀磁盤IO,一般已啟動(dòng)的時(shí)候,就會(huì)主動(dòng)換入內(nèi)存。”
復(fù)制代碼

 

舉個(gè)例子來對(duì)比。 
B樹: 

比如說,我們要查找關(guān)鍵字范圍在3到7的關(guān)鍵字,在找到第一個(gè)符合條件的數(shù)字3后,訪問完第一個(gè)關(guān)鍵字所在的塊后,得遍歷這個(gè)B樹,獲取下一個(gè)塊,直到遇到一個(gè)不符合條件的關(guān)鍵字。遍歷的過程是比較復(fù)雜的。

B+樹(葉節(jié)點(diǎn)保存數(shù)據(jù),其他的節(jié)點(diǎn) 全部存放索引): 

相比之下,B+樹的基于范圍的查詢簡(jiǎn)潔很多。由于葉子節(jié)點(diǎn)有指向下一個(gè)葉子節(jié)點(diǎn)的指針,因此從塊1到塊2的訪問,通過塊1指向塊2的指針即可。從塊2到塊3也是通過一個(gè)指針即可。

引用一篇博文中網(wǎng)友評(píng)論的一段話: 

數(shù)據(jù)庫(kù)索引采用B+樹的主要原因是B樹在提高了磁盤IO性能的同時(shí)并沒有解決元素遍歷的效率低下的問題。正是為了解決這個(gè)問題,B+樹應(yīng)運(yùn)而生。
B+樹只要遍歷葉子節(jié)點(diǎn)就可以實(shí)現(xiàn)整棵樹的遍歷。而且在數(shù)據(jù)庫(kù)中基于范圍的查詢是非常頻繁的,而B樹不支持這樣的操作(或者說效率太低)。

正如上面所說,在數(shù)據(jù)庫(kù)中基于范圍的查詢是非常頻繁的,因此MySQL最終選擇的索引結(jié)構(gòu)是B+樹而不是B樹。 

 

二、索引的原理

一 索引原理

索引的目的在于提高查詢效率,與我們查閱圖書所用的目錄是一個(gè)道理:先定位到章,然后定位到該章下的一個(gè)小節(jié),然后找到頁數(shù)。相似的例子還有:查字典,查火車車次,飛機(jī)航班等

本質(zhì)都是:通過不斷地縮小想要獲取數(shù)據(jù)的范圍來篩選出最終想要的結(jié)果,同時(shí)把隨機(jī)的事件變成順序的事件,也就是說,有了這種索引機(jī)制,我們可以總是用同一種查找方式來鎖定數(shù)據(jù)。

數(shù)據(jù)庫(kù)也是一樣,但顯然要復(fù)雜的多,因?yàn)椴粌H面臨著等值查詢,還有范圍查詢(>、<、between、in)、模糊查詢(like)、并集查詢(or)等等。數(shù)據(jù)庫(kù)應(yīng)該選擇怎么樣的方式來應(yīng)對(duì)所有的問題呢?我們回想字典的例子,能不能把數(shù)據(jù)分成段,然后分段查詢呢?最簡(jiǎn)單的如果1000條數(shù)據(jù),1到100分成第一段,101到200分成第二段,201到300分成第三段......這樣查第250條數(shù)據(jù),只要找第三段就可以了,一下子去除了90%的無效數(shù)據(jù)。但如果是1千萬的記錄呢,分成幾段比較好?稍有算法基礎(chǔ)的同學(xué)會(huì)想到搜索樹,其平均復(fù)雜度是lgN,具有不錯(cuò)的查詢性能。但這里我們忽略了一個(gè)關(guān)鍵的問題,復(fù)雜度模型是基于每次相同的操作成本來考慮的。而數(shù)據(jù)庫(kù)實(shí)現(xiàn)比較復(fù)雜,一方面數(shù)據(jù)是保存在磁盤上的,另外一方面為了提高性能,每次又可以把部分?jǐn)?shù)據(jù)讀入內(nèi)存來計(jì)算,因?yàn)槲覀冎涝L問磁盤的成本大概是訪問內(nèi)存的十萬倍左右,所以簡(jiǎn)單的搜索樹難以滿足復(fù)雜的應(yīng)用場(chǎng)景。

 二 磁盤IO與預(yù)讀

考慮到磁盤IO是非常高昂的操作,計(jì)算機(jī)操作系統(tǒng)做了一些優(yōu)化,當(dāng)一次IO時(shí),不光把當(dāng)前磁盤地址的數(shù)據(jù),而是把相鄰的數(shù)據(jù)也都讀取到內(nèi)存緩沖區(qū)內(nèi),因?yàn)榫植款A(yù)讀性原理告訴我們,當(dāng)計(jì)算機(jī)訪問一個(gè)地址的數(shù)據(jù)的時(shí)候,與其相鄰的數(shù)據(jù)也會(huì)很快被訪問到。每一次IO讀取的數(shù)據(jù)我們稱之為一頁(page)。具體一頁有多大數(shù)據(jù)跟操作系統(tǒng)有關(guān),一般為4k或8k,也就是我們讀取一頁內(nèi)的數(shù)據(jù)時(shí)候,實(shí)際上才發(fā)生了一次IO,這個(gè)理論對(duì)于索引的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)非常有幫助。

三、索引的數(shù)據(jù)結(jié)構(gòu)

任何一種數(shù)據(jù)結(jié)構(gòu)都不是憑空產(chǎn)生的,一定會(huì)有它的背景和使用場(chǎng)景,我們現(xiàn)在總結(jié)一下,我們需要這種數(shù)據(jù)結(jié)構(gòu)能夠做些什么,其實(shí)很簡(jiǎn)單,那就是:每次查找數(shù)據(jù)時(shí)把磁盤IO次數(shù)控制在一個(gè)很小的數(shù)量級(jí),最好是常數(shù)數(shù)量級(jí)。那么我們就想到如果一個(gè)高度可控的多路搜索樹是否能滿足需求呢?就這樣,b+樹應(yīng)運(yùn)而生。

如上圖,是一顆b+樹,關(guān)于b+樹的定義可以參見B+樹,這里只說一些重點(diǎn),淺藍(lán)色的塊我們稱之為一個(gè)磁盤塊,可以看到每個(gè)磁盤塊包含幾個(gè)數(shù)據(jù)項(xiàng)(深藍(lán)色所示)和指針(黃色所示),如磁盤塊1包含數(shù)據(jù)項(xiàng)17和35,包含指針P1、P2、P3,P1表示小于17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大于35的磁盤塊。真實(shí)的數(shù)據(jù)存在于葉子節(jié)點(diǎn)即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節(jié)點(diǎn)只不存儲(chǔ)真實(shí)的數(shù)據(jù),只存儲(chǔ)指引搜索方向的數(shù)據(jù)項(xiàng),如17、35并不真實(shí)存在于數(shù)據(jù)表中。

###b+樹的查找過程
如圖所示,如果要查找數(shù)據(jù)項(xiàng)29,那么首先會(huì)把磁盤塊1由磁盤加載到內(nèi)存,此時(shí)發(fā)生一次IO,在內(nèi)存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針,內(nèi)存時(shí)間因?yàn)榉浅6蹋ㄏ啾却疟P的IO)可以忽略不計(jì),通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內(nèi)存,發(fā)生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內(nèi)存,發(fā)生第三次IO,同時(shí)內(nèi)存中做二分查找找到29,結(jié)束查詢,總計(jì)三次IO。真實(shí)的情況是,3層的b+樹可以表示上百萬的數(shù)據(jù),如果上百萬的數(shù)據(jù)查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個(gè)數(shù)據(jù)項(xiàng)都要發(fā)生一次IO,那么總共需要百萬次的IO,顯然成本非常非常高。

###b+樹性質(zhì)
1.索引字段要盡量的小:通過上面的分析,我們知道IO次數(shù)取決于b+數(shù)的高度h,假設(shè)當(dāng)前數(shù)據(jù)表的數(shù)據(jù)為N,每個(gè)磁盤塊的數(shù)據(jù)項(xiàng)的數(shù)量是m,則有h=㏒(m+1)N,當(dāng)數(shù)據(jù)量N一定的情況下,m越大,h越??;而m = 磁盤塊的大小 / 數(shù)據(jù)項(xiàng)的大小,磁盤塊的大小也就是一個(gè)數(shù)據(jù)頁的大小,是固定的,如果數(shù)據(jù)項(xiàng)占的空間越小,數(shù)據(jù)項(xiàng)的數(shù)量越多,樹的高度越低。這就是為什么每個(gè)數(shù)據(jù)項(xiàng),即索引字段要盡量的小,比如int占4字節(jié),要比bigint8字節(jié)少一半。這也是為什么b+樹要求把真實(shí)的數(shù)據(jù)放到葉子節(jié)點(diǎn)而不是內(nèi)層節(jié)點(diǎn),一旦放到內(nèi)層節(jié)點(diǎn),磁盤塊的數(shù)據(jù)項(xiàng)會(huì)大幅度下降,導(dǎo)致樹增高。當(dāng)數(shù)據(jù)項(xiàng)等于1時(shí)將會(huì)退化成線性表。
2.索引的最左匹配特性(即從左往右匹配):當(dāng)b+樹的數(shù)據(jù)項(xiàng)是復(fù)合的數(shù)據(jù)結(jié)構(gòu),比如(name,age,sex)的時(shí)候,b+數(shù)是按照從左到右的順序來建立搜索樹的,比如當(dāng)(張三,20,F)這樣的數(shù)據(jù)來檢索的時(shí)候,b+樹會(huì)優(yōu)先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最后得到檢索的數(shù)據(jù);但當(dāng)(20,F)這樣的沒有name的數(shù)據(jù)來的時(shí)候,b+樹就不知道下一步該查哪個(gè)節(jié)點(diǎn),因?yàn)榻⑺阉鳂涞臅r(shí)候name就是第一個(gè)比較因子,必須要先根據(jù)name來搜索才能知道下一步去哪里查詢。比如當(dāng)(張三,F)這樣的數(shù)據(jù)來檢索時(shí),b+樹可以用name來指定搜索方向,但下一個(gè)字段age的缺失,所以只能把名字等于張三的數(shù)據(jù)都找到,然后再匹配性別是F的數(shù)據(jù)了, 這個(gè)是非常重要的性質(zhì),即索引的最左匹配特性。

這也是經(jīng)常考察的,比如 我定義了 A,B,C的聯(lián)合索引,如果 我只傳遞了 A,B 能走索引嗎?答案是能,因?yàn)樽钭髠?cè)原理(百度問過) 

補(bǔ)充一下. 全文索引(FULLTEXT)=mysql的 myISAM搜索引擎默認(rèn)的索引類型

      MySQL從3.23.23版開始支持全文索引和全文檢索,fulltext索引僅可用于 MyISAM 表;他們可以從CHAR、VARCHAR或TEXT列中作為CREATE TABLE語句的一部分被創(chuàng)建,或是隨后使用ALTER TABLE 或CREATE INDEX被添加。////對(duì)于較大的數(shù)據(jù)集,將你的資料輸入一個(gè)沒有FULLTEXT索引的表中,然后創(chuàng)建索引,其速度比把資料輸入現(xiàn)有FULLTEXT索引的速度更為快。不過切記對(duì)于大容量的數(shù)據(jù)表,生成全文索引是一個(gè)非常消耗時(shí)間非常消耗硬盤空間的做法。
       文本字段上的普通索引只能加快對(duì)出現(xiàn)在字段內(nèi)容最前面的字符串(也就是字段內(nèi)容開頭的字符)進(jìn)行檢索操作。如果字段里存放的是由幾個(gè)、甚至是多個(gè)單詞構(gòu)成的較大段文字,普通索引就沒什么作用了。這種檢索往往以LIKE %word%的形式出現(xiàn),這對(duì)MySQL來說很復(fù)雜,如果需要處理的數(shù)據(jù)量很大,響應(yīng)時(shí)間就會(huì)很長(zhǎng)。 
  這類場(chǎng)合正是全文索引(full-text index)可以大顯身手的地方。在生成這種類型的索引時(shí),MySQL將把在文本中出現(xiàn)的所有單詞創(chuàng)建為一份清單,查詢操作將根據(jù)這份清單去檢索有關(guān)的數(shù)據(jù)記錄。全文索引即可以隨數(shù)據(jù)表一同創(chuàng)建,也可以等日后有必要時(shí)再使用下面這條命令添加: 
  ALTER TABLE table_name ADD FULLTEXT(column1, column2) 
  有了全文索引,就可以用SELECT查詢命令去檢索那些包含著一個(gè)或多個(gè)給定單詞的數(shù)據(jù)記錄了。下面是這類查詢命令的基本語法: 
  SELECT * FROM table_name 
  WHERE MATCH(column1, column2) AGAINST('word1', 'word2', 'word3') 
  上面這條命令將把column1和column2字段里有word1、word2和word3的數(shù)據(jù)記錄全部查詢出來。 

參考:Mysql索引詳解及優(yōu)化(key和index區(qū)別) 

四,索引使用注意事項(xiàng)

1,不要濫用索引

①,索引提高查詢速度,卻會(huì)降低更新表的速度,因?yàn)楦卤頃r(shí),mysql不僅要更新數(shù)據(jù),保存數(shù)據(jù),還要更新索引,保存索引

②,索引會(huì)占用磁盤空間 

2,索引不會(huì)包含含有NULL值的列

復(fù)合索引只要有一列含有NULL值,那么這一列對(duì)于此符合索引就是無效的,因此我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)設(shè)計(jì)時(shí)不要讓字段的默認(rèn)值為NULL。 

3,MySQL查詢只是用一個(gè)索引

如果where字句中使用了索引的話,那么order by中的列是不會(huì)使用索引的 

4,like

like '%aaa%'不會(huì)使用索引而like "aaa%"可以使用索引


二、選擇索引的數(shù)據(jù)類型

Mysql支持很多數(shù)據(jù)類型,選擇合適的數(shù)據(jù)類型存儲(chǔ)數(shù)據(jù)對(duì)性能有很大的影響。

(1)越小的數(shù)據(jù)類型通常更好:越小的數(shù)據(jù)類型通常在磁盤、內(nèi)存和cpu緩存中都需要更少的空間,處理起來更快。

(2)簡(jiǎn)單的數(shù)據(jù)類型更好:整形數(shù)據(jù)比起字符,處理開銷更小,因?yàn)樽址谋容^更復(fù)雜。在MySQL中,應(yīng)用內(nèi)置的日期和時(shí)間數(shù)據(jù)類型,而不是字符串來存儲(chǔ)時(shí)間;以及用整形數(shù)據(jù)存儲(chǔ)IP地址。

(3)盡量避免NULL:應(yīng)該制定列為NOT NULL,除非你想存儲(chǔ)NULL。在MySQL中,含有空值的列很難進(jìn)行查詢優(yōu)化,因?yàn)樗麄兪沟盟饕?、索引的統(tǒng)計(jì)信息以及比較運(yùn)算更加復(fù)雜。

三、MySQL常見索引有:主鍵索引、唯一索引、普通索引、全文索引、組合索引

1,INDEX(普通索引):ALTER TABLE 'table_name' ADD INDEX index_name('col')

最基本的索引,沒有任何限制 

2,UNIQUE(唯一索引):ALTER TABLE 'table_name' ADD UNIQUE('col')

與“普通索引”類似,不同的就是:索引列的值必須唯一,但允許有空值。 

3,PRIMARY KEY(主鍵索引):ALTER TABLE 'table_name' ADD PRIMARY KEY('col')

是一種特殊的唯一索引,不允許有空值。 

4,F(xiàn)ULLTEXT(全文索引):ALTER TABLE 'table_name' ADD FULLTEXT('col')

僅可用于MyISAM和InoDB,針對(duì)較大的數(shù)據(jù),生成全文索引很耗時(shí)耗空間

組合索引:ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3')

為了更多的提高mysql效率可建立組合索引,遵循“最左前綴”原則。創(chuàng)建復(fù)合索引應(yīng)該將最常用(頻率)做限制條件的列放在最左邊,一次遞減。組合索引最左字段用in是可以用到索引的。相當(dāng)于建立了col1,col1col2,col1col2col3三個(gè)索引

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多