重新學(xué)習(xí)Mysql數(shù)據(jù)庫4:Mysql索引實(shí)現(xiàn)原理

MySQL索引類型

一廊散、簡介

MySQL目前主要有以下幾種索引類型:
1.普通索引
2.唯一索引
3.主鍵索引
4.組合索引
5.全文索引

二、語句

<pre>CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key]index_name[asc|desc]
</pre>

1.unique|fulltext為可選參數(shù)践叠,分別表示唯一索引、全文索引
2.index和key為同義詞,兩者作用相同嗡贺,用來指定創(chuàng)建索引
3.col_name為需要?jiǎng)?chuàng)建索引的字段列业舍,該列必須從數(shù)據(jù)表中該定義的多個(gè)列中選擇
4.index_name指定索引的名稱抖拦,為可選參數(shù),如果不指定舷暮,默認(rèn)col_name為索引值
5.length為可選參數(shù)态罪,表示索引的長度,只有字符串類型的字段才能指定索引長度
6.asc或desc指定升序或降序的索引值存儲(chǔ)

三下面、索引類型

1.普通索引
是最基本的索引复颈,它沒有任何限制。它有以下幾種創(chuàng)建方式:
(1)直接創(chuàng)建索引

<pre>CREATE INDEX index_name ON table(column(length))
</pre>

(2)修改表結(jié)構(gòu)的方式添加索引

<pre>ALTER TABLE table_name ADD INDEX index_name ON (column(length))
</pre>

(3)創(chuàng)建表的時(shí)候同時(shí)創(chuàng)建索引

復(fù)制代碼

<pre>CREATE TABLE table (
id int(11) NOT NULL AUTO_INCREMENT ,
title char(255) CHARACTER NOT NULL ,
content text CHARACTER NULL ,
time int(10) NULL DEFAULT NULL ,
PRIMARY KEY (id),
INDEX index_name (title(length))
)
</pre>

復(fù)制代碼

(4)刪除索引

<pre>DROP INDEX index_name ON table
</pre>

2.唯一索引
與前面的普通索引類似沥割,不同的就是:索引列的值必須唯一耗啦,但允許有空值。如果是組合索引机杜,則列值的組合必須唯一帜讲。它有以下幾種創(chuàng)建方式:
(1)創(chuàng)建唯一索引

<pre>CREATE UNIQUE INDEX indexName ON table(column(length))
</pre>

(2)修改表結(jié)構(gòu)

<pre>ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
</pre>

(3)創(chuàng)建表的時(shí)候直接指定

復(fù)制代碼

<pre>CREATE TABLE table (
id int(11) NOT NULL AUTO_INCREMENT ,
title char(255) CHARACTER NOT NULL ,
content text CHARACTER NULL ,
time int(10) NULL DEFAULT NULL ,
UNIQUE indexName (title(length))
);
</pre>

復(fù)制代碼

3.主鍵索引
是一種特殊的唯一索引,一個(gè)表只能有一個(gè)主鍵椒拗,不允許有空值似将。一般是在建表的時(shí)候同時(shí)創(chuàng)建主鍵索引:

<pre>CREATE TABLE table (
id int(11) NOT NULL AUTO_INCREMENT ,
title char(255) NOT NULL ,
PRIMARY KEY (id)
);
</pre>

4.組合索引
指多個(gè)字段上創(chuàng)建的索引嬉荆,只有在查詢條件中使用了創(chuàng)建索引時(shí)的第一個(gè)字段童本,索引才會(huì)被使用狸捅。使用組合索引時(shí)遵循最左前綴集合

<pre>ALTER TABLE table ADD INDEX name_city_age (name,city,age);
</pre>

5.全文索引
主要用來查找文本中的關(guān)鍵字匀借,而不是直接與索引中的值相比較沐批。fulltext索引跟其它索引大不相同岂昭,它更像是一個(gè)搜索引擎铛铁,而不是簡單的where語句的參數(shù)匹配铃将。fulltext索引配合match against操作使用兴溜,而不是一般的where語句加like侦厚。它可以在create table耻陕,alter table ,create index使用刨沦,不過目前只有char诗宣、varchar,text 列上可以創(chuàng)建全文索引想诅。值得一提的是召庞,在數(shù)據(jù)量較大時(shí)候,現(xiàn)將數(shù)據(jù)放入一個(gè)沒有全局索引的表中来破,然后再用CREATE index創(chuàng)建fulltext索引篮灼,要比先為一張表建立fulltext然后再將數(shù)據(jù)寫入的速度快很多。
(1)創(chuàng)建表的適合添加全文索引

復(fù)制代碼

<pre>CREATE TABLE table (
id int(11) NOT NULL AUTO_INCREMENT ,
title char(255) CHARACTER NOT NULL ,
content text CHARACTER NULL ,
time int(10) NULL DEFAULT NULL ,
PRIMARY KEY (id),
FULLTEXT (content)
);
</pre>

復(fù)制代碼

(2)修改表結(jié)構(gòu)添加全文索引

<pre>ALTER TABLE article ADD FULLTEXT index_content(content)
</pre>

(3)直接創(chuàng)建索引

<pre>CREATE FULLTEXT INDEX index_content ON article(content)
</pre>

四徘禁、缺點(diǎn)

1.雖然索引大大提高了查詢速度诅诱,同時(shí)卻會(huì)降低更新表的速度,如對(duì)表進(jìn)行insert送朱、update和delete娘荡。因?yàn)楦卤頃r(shí),不僅要保存數(shù)據(jù)驶沼,還要保存一下索引文件炮沐。
2.建立索引會(huì)占用磁盤空間的索引文件。一般情況這個(gè)問題不太嚴(yán)重商乎,但如果你在一個(gè)大表上創(chuàng)建了多種組合索引央拖,索引文件的會(huì)增長很快祭阀。
索引只是提高效率的一個(gè)因素鹉戚,如果有大數(shù)據(jù)量的表,就需要花時(shí)間研究建立最優(yōu)秀的索引专控,或優(yōu)化查詢語句抹凳。

五、注意事項(xiàng)

使用索引時(shí)伦腐,有以下一些技巧和注意事項(xiàng):
1.索引不會(huì)包含有null值的列
只要列中包含有null值都將不會(huì)被包含在索引中赢底,復(fù)合索引中只要有一列含有null值,那么這一列對(duì)于此復(fù)合索引就是無效的柏蘑。所以我們?cè)跀?shù)據(jù)庫設(shè)計(jì)時(shí)不要讓字段的默認(rèn)值為null幸冻。
2.使用短索引
對(duì)串列進(jìn)行索引,如果可能應(yīng)該指定一個(gè)前綴長度咳焚。例如洽损,如果有一個(gè)char(255)的列,如果在前10個(gè)或20個(gè)字符內(nèi)革半,多數(shù)值是惟一的碑定,那么就不要對(duì)整個(gè)列進(jìn)行索引流码。短索引不僅可以提高查詢速度而且可以節(jié)省磁盤空間和I/O操作。
3.索引列排序
查詢只使用一個(gè)索引延刘,因此如果where子句中已經(jīng)使用了索引的話漫试,那么order by中的列是不會(huì)使用索引的。因此數(shù)據(jù)庫默認(rèn)排序可以符合要求的情況下不要使用排序操作碘赖;盡量不要包含多個(gè)列的排序驾荣,如果需要最好給這些列創(chuàng)建復(fù)合索引。
4.like語句操作
一般情況下不推薦使用like操作普泡,如果非使用不可秘车,如何使用也是一個(gè)問題。like “%aaa%” 不會(huì)使用索引而like “aaa%”可以使用索引劫哼。
5.不要在列上進(jìn)行運(yùn)算
這將導(dǎo)致索引失效而進(jìn)行全表掃描叮趴,例如

<pre>SELECT * FROM table_name WHERE YEAR(column_name)<2017;
</pre>

6.不使用not in和<>操作

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

摘要

本文以MySQL數(shù)據(jù)庫為研究對(duì)象,討論與數(shù)據(jù)庫索引相關(guān)的一些話題权烧。特別需要說明的是眯亦,MySQL支持諸多存儲(chǔ)引擎,而各種存儲(chǔ)引擎對(duì)索引的支持也各不相同般码,因此MySQL數(shù)據(jù)庫支持多種索引類型妻率,如BTree索引,哈希索引板祝,全文索引等等宫静。為了避免混亂,本文將只關(guān)注于BTree索引券时,因?yàn)檫@是平常使用MySQL時(shí)主要打交道的索引孤里,至于哈希索引和全文索引本文暫不討論。

文章主要內(nèi)容分為三個(gè)部分橘洞。

第一部分主要從數(shù)據(jù)結(jié)構(gòu)及算法理論層面討論MySQL數(shù)據(jù)庫索引的數(shù)理基礎(chǔ)捌袜。

第二部分結(jié)合MySQL數(shù)據(jù)庫中MyISAM和InnoDB數(shù)據(jù)存儲(chǔ)引擎中索引的架構(gòu)實(shí)現(xiàn)討論聚集索引、非聚集索引及覆蓋索引等話題炸枣。

第三部分根據(jù)上面的理論基礎(chǔ)虏等,討論MySQL中高性能使用索引的策略。

數(shù)據(jù)結(jié)構(gòu)及算法基礎(chǔ)

索引的本質(zhì)

MySQL官方對(duì)索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)适肠。提取句子主干霍衫,就可以得到索引的本質(zhì):索引是數(shù)據(jù)結(jié)構(gòu)。

我們知道侯养,數(shù)據(jù)庫查詢是數(shù)據(jù)庫的最主要功能之一敦跌。我們都希望查詢數(shù)據(jù)的速度能盡可能的快,因此數(shù)據(jù)庫系統(tǒng)的設(shè)計(jì)者會(huì)從查詢算法的角度進(jìn)行優(yōu)化沸毁。最基本的查詢算法當(dāng)然是順序查找(linear search)峰髓,這種復(fù)雜度為O(n)的算法在數(shù)據(jù)量很大時(shí)顯然是糟糕的傻寂,好在計(jì)算機(jī)科學(xué)的發(fā)展提供了很多更優(yōu)秀的查找算法,例如二分查找(binary search)携兵、二叉樹查找(binary tree search)等疾掰。如果稍微分析一下會(huì)發(fā)現(xiàn),每種查找算法都只能應(yīng)用于特定的數(shù)據(jù)結(jié)構(gòu)之上徐紧,例如二分查找要求被檢索數(shù)據(jù)有序静檬,而二叉樹查找只能應(yīng)用于二叉查找樹上,但是數(shù)據(jù)本身的組織結(jié)構(gòu)不可能完全滿足各種數(shù)據(jù)結(jié)構(gòu)(例如并级,理論上不可能同時(shí)將兩列都按順序進(jìn)行組織)拂檩,所以,在數(shù)據(jù)之外嘲碧,數(shù)據(jù)庫系統(tǒng)還維護(hù)著滿足特定查找算法的數(shù)據(jù)結(jié)構(gòu)稻励,這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用(指向)數(shù)據(jù),這樣就可以在這些數(shù)據(jù)結(jié)構(gòu)上實(shí)現(xiàn)高級(jí)查找算法愈涩。這種數(shù)據(jù)結(jié)構(gòu)望抽,就是索引。

看一個(gè)例子:

圖1

圖1展示了一種可能的索引方式履婉。左邊是數(shù)據(jù)表煤篙,一共有兩列七條記錄,最左邊的是數(shù)據(jù)記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)毁腿。為了加快Col2的查找辑奈,可以維護(hù)一個(gè)右邊所示的二叉查找樹,每個(gè)節(jié)點(diǎn)分別包含索引鍵值和一個(gè)指向?qū)?yīng)數(shù)據(jù)記錄物理地址的指針已烤,這樣就可以運(yùn)用二叉查找在O(log2n)O(log2n)的復(fù)雜度內(nèi)獲取到相應(yīng)數(shù)據(jù)鸠窗。

雖然這是一個(gè)貨真價(jià)實(shí)的索引,但是實(shí)際的數(shù)據(jù)庫系統(tǒng)幾乎沒有使用二叉查找樹或其進(jìn)化品種紅黑樹(red-black tree)實(shí)現(xiàn)的草戈,原因會(huì)在下文介紹塌鸯。

B-Tree和B+Tree

目前大部分?jǐn)?shù)據(jù)庫系統(tǒng)及文件系統(tǒng)都采用B-Tree或其變種B+Tree作為索引結(jié)構(gòu)侍瑟,在本文的下一節(jié)會(huì)結(jié)合存儲(chǔ)器原理及計(jì)算機(jī)存取原理討論為什么B-Tree和B+Tree在被如此廣泛用于索引唐片,這一節(jié)先單純從數(shù)據(jù)結(jié)構(gòu)角度描述它們。

B-Tree

為了描述B-Tree涨颜,首先定義一條數(shù)據(jù)記錄為一個(gè)二元組[key, data]费韭,key為記錄的鍵值,對(duì)于不同數(shù)據(jù)記錄庭瑰,key是互不相同的星持;data為數(shù)據(jù)記錄除key外的數(shù)據(jù)。那么B-Tree是滿足下列條件的數(shù)據(jù)結(jié)構(gòu):

d為大于1的一個(gè)正整數(shù)弹灭,稱為B-Tree的度督暂。

h為一個(gè)正整數(shù)揪垄,稱為B-Tree的高度。

每個(gè)非葉子節(jié)點(diǎn)由n-1個(gè)key和n個(gè)指針組成逻翁,其中d<=n<=2d饥努。

每個(gè)葉子節(jié)點(diǎn)最少包含一個(gè)key和兩個(gè)指針,最多包含2d-1個(gè)key和2d個(gè)指針八回,葉節(jié)點(diǎn)的指針均為null 酷愧。

所有葉節(jié)點(diǎn)具有相同的深度,等于樹高h(yuǎn)缠诅。

key和指針互相間隔溶浴,節(jié)點(diǎn)兩端是指針。

一個(gè)節(jié)點(diǎn)中的key從左到右非遞減排列管引。

所有節(jié)點(diǎn)組成樹結(jié)構(gòu)士败。

每個(gè)指針要么為null,要么指向另外一個(gè)節(jié)點(diǎn)褥伴。

如果某個(gè)指針在節(jié)點(diǎn)node最左邊且不為null拱烁,則其指向節(jié)點(diǎn)的所有key小于v(key1)v(key1),其中v(key1)v(key1)為node的第一個(gè)key的值噩翠。

如果某個(gè)指針在節(jié)點(diǎn)node最右邊且不為null戏自,則其指向節(jié)點(diǎn)的所有key大于v(keym)v(keym),其中v(keym)v(keym)為node的最后一個(gè)key的值伤锚。

如果某個(gè)指針在節(jié)點(diǎn)node的左右相鄰key分別是keyikeyi和keyi+1keyi+1且不為null擅笔,則其指向節(jié)點(diǎn)的所有key小于v(keyi+1)v(keyi+1)且大于v(keyi)v(keyi)。

圖2是一個(gè)d=2的B-Tree示意圖屯援。

圖2

由于B-Tree的特性猛们,在B-Tree中按key檢索數(shù)據(jù)的算法非常直觀:首先從根節(jié)點(diǎn)進(jìn)行二分查找,如果找到則返回對(duì)應(yīng)節(jié)點(diǎn)的data狞洋,否則對(duì)相應(yīng)區(qū)間的指針指向的節(jié)點(diǎn)遞歸進(jìn)行查找弯淘,直到找到節(jié)點(diǎn)或找到null指針,前者查找成功吉懊,后者查找失敗庐橙。B-Tree上查找算法的偽代碼如下:

  1. BTree_Search(node, key) {
  2. if(node == null) return null;
  3. foreach(node.key)
  4. {
  5. if(node.key[i] == key) return node.data[i];
  6. if(node.key[i] > key) return BTree_Search(point[i]->node);
  7. }
  8. return BTree_Search(point[i+1]->node);
  9. }
  10. data = BTree_Search(root, my_key);

關(guān)于B-Tree有一系列有趣的性質(zhì),例如一個(gè)度為d的B-Tree借嗽,設(shè)其索引N個(gè)key态鳖,則其樹高h(yuǎn)的上限為logd((N+1)/2)logd((N+1)/2),檢索一個(gè)key恶导,其查找節(jié)點(diǎn)個(gè)數(shù)的漸進(jìn)復(fù)雜度為O(logdN)O(logdN)浆竭。從這點(diǎn)可以看出,B-Tree是一個(gè)非常有效率的索引數(shù)據(jù)結(jié)構(gòu)。

另外邦泄,由于插入刪除新的數(shù)據(jù)記錄會(huì)破壞B-Tree的性質(zhì)删窒,因此在插入刪除時(shí),需要對(duì)樹進(jìn)行一個(gè)分裂顺囊、合并易稠、轉(zhuǎn)移等操作以保持B-Tree性質(zhì),本文不打算完整討論B-Tree這些內(nèi)容包蓝,因?yàn)橐呀?jīng)有許多資料詳細(xì)說明了B-Tree的數(shù)學(xué)性質(zhì)及插入刪除算法驶社,有興趣的朋友可以在本文末的參考文獻(xiàn)一欄找到相應(yīng)的資料進(jìn)行閱讀。

B+Tree

B-Tree有許多變種测萎,其中最常見的是B+Tree亡电,例如MySQL就普遍使用B+Tree實(shí)現(xiàn)其索引結(jié)構(gòu)。

與B-Tree相比硅瞧,B+Tree有以下不同點(diǎn):

每個(gè)節(jié)點(diǎn)的指針上限為2d而不是2d+1份乒。

內(nèi)節(jié)點(diǎn)不存儲(chǔ)data,只存儲(chǔ)key腕唧;葉子節(jié)點(diǎn)不存儲(chǔ)指針或辖。

圖3是一個(gè)簡單的B+Tree示意。

圖3

由于并不是所有節(jié)點(diǎn)都具有相同的域枣接,因此B+Tree中葉節(jié)點(diǎn)和內(nèi)節(jié)點(diǎn)一般大小不同颂暇。這點(diǎn)與B-Tree不同,雖然B-Tree中不同節(jié)點(diǎn)存放的key和指針可能數(shù)量不一致但惶,但是每個(gè)節(jié)點(diǎn)的域和上限是一致的耳鸯,所以在實(shí)現(xiàn)中B-Tree往往對(duì)每個(gè)節(jié)點(diǎn)申請(qǐng)同等大小的空間。

一般來說膀曾,B+Tree比B-Tree更適合實(shí)現(xiàn)外存儲(chǔ)索引結(jié)構(gòu)县爬,具體原因與外存儲(chǔ)器原理及計(jì)算機(jī)存取原理有關(guān),將在下面討論添谊。

帶有順序訪問指針的B+Tree

一般在數(shù)據(jù)庫系統(tǒng)或文件系統(tǒng)中使用的B+Tree結(jié)構(gòu)都在經(jīng)典B+Tree的基礎(chǔ)上進(jìn)行了優(yōu)化财喳,增加了順序訪問指針。

圖4

如圖4所示斩狱,在B+Tree的每個(gè)葉子節(jié)點(diǎn)增加一個(gè)指向相鄰葉子節(jié)點(diǎn)的指針耳高,就形成了帶有順序訪問指針的B+Tree。做這個(gè)優(yōu)化的目的是為了提高區(qū)間訪問的性能喊废,例如圖4中如果要查詢key為從18到49的所有數(shù)據(jù)記錄祝高,當(dāng)找到18后,只需順著節(jié)點(diǎn)和指針順序遍歷就可以一次性訪問到所有數(shù)據(jù)節(jié)點(diǎn)污筷,極大提到了區(qū)間查詢效率。

這一節(jié)對(duì)B-Tree和B+Tree進(jìn)行了一個(gè)簡單的介紹,下一節(jié)結(jié)合存儲(chǔ)器存取原理介紹為什么目前B+Tree是數(shù)據(jù)庫系統(tǒng)實(shí)現(xiàn)索引的首選數(shù)據(jù)結(jié)構(gòu)瓣蛀。

為什么使用B-Tree(B+Tree)

上文說過陆蟆,紅黑樹等數(shù)據(jù)結(jié)構(gòu)也可以用來實(shí)現(xiàn)索引,但是文件系統(tǒng)及數(shù)據(jù)庫系統(tǒng)普遍采用B-/+Tree作為索引結(jié)構(gòu)惋增,這一節(jié)將結(jié)合計(jì)算機(jī)組成原理相關(guān)知識(shí)討論B-/+Tree作為索引的理論基礎(chǔ)叠殷。

一般來說,索引本身也很大诈皿,不可能全部存儲(chǔ)在內(nèi)存中林束,因此索引往往以索引文件的形式存儲(chǔ)的磁盤上。這樣的話稽亏,索引查找過程中就要產(chǎn)生磁盤I/O消耗壶冒,相對(duì)于內(nèi)存存取,I/O存取的消耗要高幾個(gè)數(shù)量級(jí)截歉,所以評(píng)價(jià)一個(gè)數(shù)據(jù)結(jié)構(gòu)作為索引的優(yōu)劣最重要的指標(biāo)就是在查找過程中磁盤I/O操作次數(shù)的漸進(jìn)復(fù)雜度胖腾。換句話說,索引的結(jié)構(gòu)組織要盡量減少查找過程中磁盤I/O的存取次數(shù)瘪松。下面先介紹內(nèi)存和磁盤存取原理咸作,然后再結(jié)合這些原理分析B-/+Tree作為索引的效率。

B樹

為什么要B樹

磁盤中有兩個(gè)機(jī)械運(yùn)動(dòng)的部分宵睦,分別是盤片旋轉(zhuǎn)和磁臂移動(dòng)记罚。盤片旋轉(zhuǎn)就是我們市面上所提到的多少轉(zhuǎn)每分鐘,而磁盤移動(dòng)則是在盤片旋轉(zhuǎn)到指定位置以后壳嚎,移動(dòng)磁臂后開始進(jìn)行數(shù)據(jù)的讀寫毫胜。那么這就存在一個(gè)定位到磁盤中的塊的過程,而定位是磁盤的存取中花費(fèi)時(shí)間比較大的一塊诬辈,畢竟機(jī)械運(yùn)動(dòng)花費(fèi)的時(shí)候要遠(yuǎn)遠(yuǎn)大于電子運(yùn)動(dòng)的時(shí)間酵使。當(dāng)大規(guī)模數(shù)據(jù)存儲(chǔ)到磁盤中的時(shí)候,顯然定位是一個(gè)非潮涸悖花費(fèi)時(shí)間的過程口渔,但是我們可以通過B樹進(jìn)行優(yōu)化,提高磁盤讀取時(shí)定位的效率穿撮。

為什么B類樹可以進(jìn)行優(yōu)化呢缺脉?我們可以根據(jù)B類樹的特點(diǎn),構(gòu)造一個(gè)多階的B類樹悦穿,然后在盡量多的在結(jié)點(diǎn)上存儲(chǔ)相關(guān)的信息攻礼,保證層數(shù)盡量的少,以便后面我們可以更快的找到信息栗柒,磁盤的I/O操作也少一些礁扮,而且B類樹是平衡樹,每個(gè)結(jié)點(diǎn)到葉子結(jié)點(diǎn)的高度都是相同,這也保證了每個(gè)查詢是穩(wěn)定的太伊。

簡介

這里的B樹雇锡,也就是英文中的B-Tree,一個(gè) m 階的B樹滿足以下條件:

  1. 每個(gè)結(jié)點(diǎn)至多擁有m棵子樹僚焦;
  2. 根結(jié)點(diǎn)至少擁有兩顆子樹(存在子樹的情況下)锰提;
  3. 除了根結(jié)點(diǎn)以外,其余每個(gè)分支結(jié)點(diǎn)至少擁有 m/2 棵子樹芳悲;
  4. 所有的葉結(jié)點(diǎn)都在同一層上立肘;
  5. 有 k 棵子樹的分支結(jié)點(diǎn)則存在 k-1 個(gè)關(guān)鍵碼,關(guān)鍵碼按照遞增次序進(jìn)行排列名扛;
  6. 關(guān)鍵字?jǐn)?shù)量需要滿足ceil(m/2)-1 <= n <= m-1谅年;

舉個(gè)栗子:

B樹上大部分的操作所需要的磁盤存取次數(shù)和B樹的高度是成正比的,在B樹中可以檢查多個(gè)子結(jié)點(diǎn)罢洲,由于在一棵樹中檢查任意一個(gè)結(jié)點(diǎn)都需要一次磁盤訪問踢故,所以B樹避免了大量的磁盤訪問。

操作

既然是樹惹苗,那么必不可少的操作就是插入和刪除殿较,這也是B樹和其它數(shù)據(jù)結(jié)構(gòu)不同的地方,當(dāng)然了桩蓉,還有必不可少的搜索淋纲,分享一個(gè)對(duì)B樹的操作進(jìn)行可視化的網(wǎng)址,它是由usfca提供的院究。

假定對(duì)高度為h的m階B樹進(jìn)行操作洽瞬。

插入

新結(jié)點(diǎn)一般插在第h層,通過搜索找到對(duì)應(yīng)的結(jié)點(diǎn)進(jìn)行插入业汰,那么根據(jù)即將插入的結(jié)點(diǎn)的數(shù)量又分為下面幾種情況伙窃。

  • 如果該結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)沒有到達(dá)m-1個(gè),那么直接插入即可样漆;
  • 如果該結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)已經(jīng)到達(dá)了m-1個(gè)为障,那么根據(jù)B樹的性質(zhì)顯然無法滿足,需要將其進(jìn)行分裂放祟。分裂的規(guī)則是該結(jié)點(diǎn)分成兩半鳍怨,將中間的關(guān)鍵字進(jìn)行提升,加入到父親結(jié)點(diǎn)中跪妥,但是這又可能存在父親結(jié)點(diǎn)也滿員的情況鞋喇,則不得不向上進(jìn)行回溯,甚至是要對(duì)根結(jié)點(diǎn)進(jìn)行分裂眉撵,那么整棵樹都加了一層侦香。

其過程如下:

刪除

同樣的落塑,我們需要先通過搜索找到相應(yīng)的值,存在則進(jìn)行刪除鄙皇,需要考慮刪除以后的情況芜赌,

  • 如果該結(jié)點(diǎn)擁有關(guān)鍵字?jǐn)?shù)量仍然滿足B樹性質(zhì)仰挣,則不做任何處理伴逸;
  • 如果該結(jié)點(diǎn)在刪除關(guān)鍵字以后不滿足B樹的性質(zhì)(關(guān)鍵字沒有到達(dá)ceil(m/2)-1的數(shù)量),則需要向兄弟結(jié)點(diǎn)借關(guān)鍵字膘壶,這有分為兄弟結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量是否足夠的情況错蝴。
    • 如果兄弟結(jié)點(diǎn)的關(guān)鍵字足夠借給該結(jié)點(diǎn),則過程為將父親結(jié)點(diǎn)的關(guān)鍵字下移颓芭,兄弟結(jié)點(diǎn)的關(guān)鍵字上移顷锰;
    • 如果兄弟結(jié)點(diǎn)的關(guān)鍵字在借出去以后也無法滿足情況,即之前兄弟結(jié)點(diǎn)的關(guān)鍵字的數(shù)量為ceil(m/2)-1亡问,借的一方的關(guān)鍵字?jǐn)?shù)量為ceil(m/2)-2的情況官紫,那么我們可以將該結(jié)點(diǎn)合并到兄弟結(jié)點(diǎn)中,合并之后的子結(jié)點(diǎn)數(shù)量少了一個(gè)州藕,則需要將父親結(jié)點(diǎn)的關(guān)鍵字下放束世,如果父親結(jié)點(diǎn)不滿足性質(zhì),則向上回溯床玻;
  • 其余情況參照BST中的刪除毁涉。

其過程如下:

B+樹

為什么要B+樹

由于B+樹的數(shù)據(jù)都存儲(chǔ)在葉子結(jié)點(diǎn)中,分支結(jié)點(diǎn)均為索引锈死,方便掃庫贫堰,只需要掃一遍葉子結(jié)點(diǎn)即可,但是B樹因?yàn)槠浞种ЫY(jié)點(diǎn)同樣存儲(chǔ)著數(shù)據(jù)待牵,我們要找到具體的數(shù)據(jù)其屏,需要進(jìn)行一次中序遍歷按序來掃,所以B+樹更加適合在區(qū)間查詢的情況缨该,所以通常B+樹用于數(shù)據(jù)庫索引偎行,而B樹則常用于文件索引。

簡介

同樣的压彭,以一個(gè)m階樹為例:

  1. 根結(jié)點(diǎn)只有一個(gè)睦优,分支數(shù)量范圍為[2,m]壮不;
  2. 分支結(jié)點(diǎn)汗盘,每個(gè)結(jié)點(diǎn)包含分支數(shù)范圍為[ceil(m/2), m];
  3. 分支結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量等于其子分支的數(shù)量減一询一,關(guān)鍵字的數(shù)量范圍為[ceil(m/2)-1, m-1]隐孽,關(guān)鍵字順序遞增癌椿;
  4. 所有葉子結(jié)點(diǎn)都在同一層;

操作

其操作和B樹的操作是類似的菱阵,不過需要注意的是踢俄,在增加值的時(shí)候,如果存在滿員的情況晴及,將選擇結(jié)點(diǎn)中的值作為新的索引都办,還有在刪除值的時(shí)候,索引中的關(guān)鍵字并不會(huì)刪除虑稼,也不會(huì)存在父親結(jié)點(diǎn)的關(guān)鍵字下沉的情況琳钉,因?yàn)槟侵皇撬饕?/p>

B樹和B+樹的區(qū)別

這都是由于B+樹和B具有這不同的存儲(chǔ)結(jié)構(gòu)所造成的區(qū)別,以一個(gè)m階樹為例蛛倦。

  1. 關(guān)鍵字的數(shù)量不同歌懒;B+樹中分支結(jié)點(diǎn)有m個(gè)關(guān)鍵字,其葉子結(jié)點(diǎn)也有m個(gè)溯壶,其關(guān)鍵字只是起到了一個(gè)索引的作用及皂,但是B樹雖然也有m個(gè)子結(jié)點(diǎn),但是其只擁有m-1個(gè)關(guān)鍵字且改。
  2. 存儲(chǔ)的位置不同验烧;B+樹中的數(shù)據(jù)都存儲(chǔ)在葉子結(jié)點(diǎn)上,也就是其所有葉子結(jié)點(diǎn)的數(shù)據(jù)組合起來就是完整的數(shù)據(jù)钾虐,但是B樹的數(shù)據(jù)存儲(chǔ)在每一個(gè)結(jié)點(diǎn)中噪窘,并不僅僅存儲(chǔ)在葉子結(jié)點(diǎn)上。
  3. 分支結(jié)點(diǎn)的構(gòu)造不同效扫;B+樹的分支結(jié)點(diǎn)僅僅存儲(chǔ)著關(guān)鍵字信息和兒子的指針(這里的指針指的是磁盤塊的偏移量)倔监,也就是說內(nèi)部結(jié)點(diǎn)僅僅包含著索引信息。
  4. 查詢不同菌仁;B樹在找到具體的數(shù)值以后浩习,則結(jié)束,而B+樹則需要通過索引找到葉子結(jié)點(diǎn)中的數(shù)據(jù)才結(jié)束济丘,也就是說B+樹的搜索過程中走了一條從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的路徑谱秽。

主存存取原理

目前計(jì)算機(jī)使用的主存基本都是隨機(jī)讀寫存儲(chǔ)器(RAM),現(xiàn)代RAM的結(jié)構(gòu)和存取原理比較復(fù)雜摹迷,這里本文拋卻具體差別疟赊,抽象出一個(gè)十分簡單的存取模型來說明RAM的工作原理。

圖5

從抽象角度看峡碉,主存是一系列的存儲(chǔ)單元組成的矩陣近哟,每個(gè)存儲(chǔ)單元存儲(chǔ)固定大小的數(shù)據(jù)。每個(gè)存儲(chǔ)單元有唯一的地址鲫寄,現(xiàn)代主存的編址規(guī)則比較復(fù)雜吉执,這里將其簡化成一個(gè)二維地址:通過一個(gè)行地址和一個(gè)列地址可以唯一定位到一個(gè)存儲(chǔ)單元疯淫。圖5展示了一個(gè)4 x 4的主存模型。

主存的存取過程如下:

當(dāng)系統(tǒng)需要讀取主存時(shí)戳玫,則將地址信號(hào)放到地址總線上傳給主存熙掺,主存讀到地址信號(hào)后,解析信號(hào)并定位到指定存儲(chǔ)單元咕宿,然后將此存儲(chǔ)單元數(shù)據(jù)放到數(shù)據(jù)總線上币绩,供其它部件讀取。

寫主存的過程類似荠列,系統(tǒng)將要寫入單元地址和數(shù)據(jù)分別放在地址總線和數(shù)據(jù)總線上类浪,主存讀取兩個(gè)總線的內(nèi)容载城,做相應(yīng)的寫操作肌似。

這里可以看出,主存存取的時(shí)間僅與存取次數(shù)呈線性關(guān)系诉瓦,因?yàn)椴淮嬖跈C(jī)械操作川队,兩次存取的數(shù)據(jù)的“距離”不會(huì)對(duì)時(shí)間有任何影響,例如睬澡,先取A0再取A1和先取A0再取D3的時(shí)間消耗是一樣的固额。

磁盤存取原理

上文說過,索引一般以文件形式存儲(chǔ)在磁盤上煞聪,索引檢索需要磁盤I/O操作斗躏。與主存不同,磁盤I/O存在機(jī)械運(yùn)動(dòng)耗費(fèi)昔脯,因此磁盤I/O的時(shí)間消耗是巨大的啄糙。

圖6是磁盤的整體結(jié)構(gòu)示意圖。

圖6

一個(gè)磁盤由大小相同且同軸的圓形盤片組成云稚,磁盤可以轉(zhuǎn)動(dòng)(各個(gè)磁盤必須同步轉(zhuǎn)動(dòng))隧饼。在磁盤的一側(cè)有磁頭支架,磁頭支架固定了一組磁頭静陈,每個(gè)磁頭負(fù)責(zé)存取一個(gè)磁盤的內(nèi)容燕雁。磁頭不能轉(zhuǎn)動(dòng),但是可以沿磁盤半徑方向運(yùn)動(dòng)(實(shí)際是斜切向運(yùn)動(dòng))鲸拥,每個(gè)磁頭同一時(shí)刻也必須是同軸的拐格,即從正上方向下看,所有磁頭任何時(shí)候都是重疊的(不過目前已經(jīng)有多磁頭獨(dú)立技術(shù)刑赶,可不受此限制)捏浊。

圖7是磁盤結(jié)構(gòu)的示意圖。

圖7

盤片被劃分成一系列同心環(huán)角撞,圓心是盤片中心呛伴,每個(gè)同心環(huán)叫做一個(gè)磁道勃痴,所有半徑相同的磁道組成一個(gè)柱面。磁道被沿半徑線劃分成一個(gè)個(gè)小的段热康,每個(gè)段叫做一個(gè)扇區(qū)沛申,每個(gè)扇區(qū)是磁盤的最小存儲(chǔ)單元。為了簡單起見姐军,我們下面假設(shè)磁盤只有一個(gè)盤片和一個(gè)磁頭铁材。

當(dāng)需要從磁盤讀取數(shù)據(jù)時(shí),系統(tǒng)會(huì)將數(shù)據(jù)邏輯地址傳給磁盤奕锌,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址著觉,即確定要讀的數(shù)據(jù)在哪個(gè)磁道,哪個(gè)扇區(qū)惊暴。為了讀取這個(gè)扇區(qū)的數(shù)據(jù)饼丘,需要將磁頭放到這個(gè)扇區(qū)上方,為了實(shí)現(xiàn)這一點(diǎn)辽话,磁頭需要移動(dòng)對(duì)準(zhǔn)相應(yīng)磁道肄鸽,這個(gè)過程叫做尋道,所耗費(fèi)時(shí)間叫做尋道時(shí)間油啤,然后磁盤旋轉(zhuǎn)將目標(biāo)扇區(qū)旋轉(zhuǎn)到磁頭下典徘,這個(gè)過程耗費(fèi)的時(shí)間叫做旋轉(zhuǎn)時(shí)間。

局部性原理與磁盤預(yù)讀

由于存儲(chǔ)介質(zhì)的特性益咬,磁盤本身存取就比主存慢很多逮诲,再加上機(jī)械運(yùn)動(dòng)耗費(fèi)纫溃,磁盤的存取速度往往是主存的幾百分分之一虑凛,因此為了提高效率,要盡量減少磁盤I/O溯街。為了達(dá)到這個(gè)目的评腺,磁盤往往不是嚴(yán)格按需讀取帘瞭,而是每次都會(huì)預(yù)讀,即使只需要一個(gè)字節(jié)蒿讥,磁盤也會(huì)從這個(gè)位置開始蝶念,順序向后讀取一定長度的數(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效率马昙。

預(yù)讀的長度一般為頁(page)的整倍數(shù)桃犬。頁是計(jì)算機(jī)管理存儲(chǔ)器的邏輯塊刹悴,硬件及操作系統(tǒng)往往將主存和磁盤存儲(chǔ)區(qū)分割為連續(xù)的大小相等的塊,每個(gè)存儲(chǔ)塊稱為一頁(在許多操作系統(tǒng)中攒暇,頁得大小通常為4k)土匀,主存和磁盤以頁為單位交換數(shù)據(jù)。當(dāng)程序要讀取的數(shù)據(jù)不在主存中時(shí)形用,會(huì)觸發(fā)一個(gè)缺頁異常就轧,此時(shí)系統(tǒng)會(huì)向磁盤發(fā)出讀盤信號(hào),磁盤會(huì)找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內(nèi)存中田度,然后異常返回妒御,程序繼續(xù)運(yùn)行。

B-/+Tree索引的性能分析

到這里終于可以分析B-/+Tree索引的性能了镇饺。

上文說過一般使用磁盤I/O次數(shù)評(píng)價(jià)索引結(jié)構(gòu)的優(yōu)劣乎莉。先從B-Tree分析,根據(jù)B-Tree的定義兰怠,可知檢索一次最多需要訪問h個(gè)節(jié)點(diǎn)梦鉴。數(shù)據(jù)庫系統(tǒng)的設(shè)計(jì)者巧妙利用了磁盤預(yù)讀原理,將一個(gè)節(jié)點(diǎn)的大小設(shè)為等于一個(gè)頁揭保,這樣每個(gè)節(jié)點(diǎn)只需要一次I/O就可以完全載入。為了達(dá)到這個(gè)目的魄宏,在實(shí)際實(shí)現(xiàn)B-Tree還需要使用如下技巧:

每次新建節(jié)點(diǎn)時(shí)秸侣,直接申請(qǐng)一個(gè)頁的空間,這樣就保證一個(gè)節(jié)點(diǎn)物理上也存儲(chǔ)在一個(gè)頁里宠互,加之計(jì)算機(jī)存儲(chǔ)分配都是按頁對(duì)齊的味榛,就實(shí)現(xiàn)了一個(gè)node只需一次I/O。

B-Tree中一次檢索最多需要h-1次I/O(根節(jié)點(diǎn)常駐內(nèi)存)予跌,漸進(jìn)復(fù)雜度為O(h)=O(logdN)O(h)=O(logdN)搏色。一般實(shí)際應(yīng)用中,出度d是非常大的數(shù)字券册,通常超過100频轿,因此h非常小(通常不超過3)烁焙。

綜上所述航邢,用B-Tree作為索引結(jié)構(gòu)效率是非常高的。

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

上文還說過,B+Tree更適合外存索引勒极,原因和內(nèi)節(jié)點(diǎn)出度d有關(guān)跨细。從上面分析可以看到,d越大索引的性能越好河质,而出度的上限取決于節(jié)點(diǎn)內(nèi)key和data的大屑讲选:

dmax=floor(pagesize/(keysize+datasize+pointsize))dmax=floor(pagesize/(keysize+datasize+pointsize))

floor表示向下取整。由于B+Tree內(nèi)節(jié)點(diǎn)去掉了data域掀鹅,因此可以擁有更大的出度散休,擁有更好的性能。

這一章從理論角度討論了與索引相關(guān)的數(shù)據(jù)結(jié)構(gòu)與算法問題乐尊,下一章將討論B+Tree是如何具體實(shí)現(xiàn)為MySQL中索引戚丸,同時(shí)將結(jié)合MyISAM和InnDB存儲(chǔ)引擎介紹非聚集索引和聚集索引兩種不同的索引實(shí)現(xiàn)形式。

MySQL索引實(shí)現(xiàn)

在MySQL中扔嵌,索引屬于存儲(chǔ)引擎級(jí)別的概念限府,不同存儲(chǔ)引擎對(duì)索引的實(shí)現(xiàn)方式是不同的,本文主要討論MyISAM和InnoDB兩個(gè)存儲(chǔ)引擎的索引實(shí)現(xiàn)方式痢缎。

MyISAM索引實(shí)現(xiàn)

MyISAM引擎使用B+Tree作為索引結(jié)構(gòu)胁勺,葉節(jié)點(diǎn)的data域存放的是數(shù)據(jù)記錄的地址。下圖是MyISAM索引的原理圖:

圖8

這里設(shè)表一共有三列独旷,假設(shè)我們以Col1為主鍵署穗,則圖8是一個(gè)MyISAM表的主索引(Primary key)示意∏锻荩可以看出MyISAM的索引文件僅僅保存數(shù)據(jù)記錄的地址案疲。在MyISAM中,主索引和輔助索引(Secondary key)在結(jié)構(gòu)上沒有任何區(qū)別麻养,只是主索引要求key是唯一的褐啡,而輔助索引的key可以重復(fù)。如果我們?cè)贑ol2上建立一個(gè)輔助索引鳖昌,則此索引的結(jié)構(gòu)如下圖所示:

圖9

同樣也是一顆B+Tree备畦,data域保存數(shù)據(jù)記錄的地址。因此遗遵,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引萍恕,如果指定的Key存在,則取出其data域的值车要,然后以data域的值為地址允粤,讀取相應(yīng)數(shù)據(jù)記錄。

MyISAM的索引方式也叫做“非聚集”的,之所以這么稱呼是為了與InnoDB的聚集索引區(qū)分类垫。

InnoDB索引實(shí)現(xiàn)

雖然InnoDB也使用B+Tree作為索引結(jié)構(gòu)司光,但具體實(shí)現(xiàn)方式卻與MyISAM截然不同。

第一個(gè)重大區(qū)別是InnoDB的數(shù)據(jù)文件本身就是索引文件悉患。從上文知道残家,MyISAM索引文件和數(shù)據(jù)文件是分離的,索引文件僅保存數(shù)據(jù)記錄的地址售躁。而在InnoDB中坞淮,表數(shù)據(jù)文件本身就是按B+Tree組織的一個(gè)索引結(jié)構(gòu),這棵樹的葉節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄陪捷。這個(gè)索引的key是數(shù)據(jù)表的主鍵回窘,因此InnoDB表數(shù)據(jù)文件本身就是主索引。

圖10

圖10是InnoDB主索引(同時(shí)也是數(shù)據(jù)文件)的示意圖市袖,可以看到葉節(jié)點(diǎn)包含了完整的數(shù)據(jù)記錄啡直。這種索引叫做聚集索引。因?yàn)镮nnoDB的數(shù)據(jù)文件本身要按主鍵聚集苍碟,所以InnoDB要求表必須有主鍵(MyISAM可以沒有)酒觅,如果沒有顯式指定,則MySQL系統(tǒng)會(huì)自動(dòng)選擇一個(gè)可以唯一標(biāo)識(shí)數(shù)據(jù)記錄的列作為主鍵微峰,如果不存在這種列舷丹,則MySQL自動(dòng)為InnoDB表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長度為6個(gè)字節(jié)县忌,類型為長整形掂榔。

第二個(gè)與MyISAM索引的不同是InnoDB的輔助索引data域存儲(chǔ)相應(yīng)記錄主鍵的值而不是地址。換句話說症杏,InnoDB的所有輔助索引都引用主鍵作為data域。例如瑞信,圖11為定義在Col3上的一個(gè)輔助索引:

圖11

這里以英文字符的ASCII碼作為比較準(zhǔn)則厉颤。聚集索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵凡简,然后用主鍵到主索引中檢索獲得記錄逼友。

了解不同存儲(chǔ)引擎的索引實(shí)現(xiàn)方式對(duì)于正確使用和優(yōu)化索引都非常有幫助,例如知道了InnoDB的索引實(shí)現(xiàn)后秤涩,就很容易明白為什么不建議使用過長的字段作為主鍵帜乞,因?yàn)樗休o助索引都引用主索引,過長的主索引會(huì)令輔助索引變得過大筐眷。再例如黎烈,用非單調(diào)的字段作為主鍵在InnoDB中不是個(gè)好主意,因?yàn)镮nnoDB數(shù)據(jù)文件本身是一顆B+Tree,非單調(diào)的主鍵會(huì)造成在插入新記錄時(shí)數(shù)據(jù)文件為了維持B+Tree的特性而頻繁的分裂調(diào)整照棋,十分低效资溃,而使用自增字段作為主鍵則是一個(gè)很好的選擇。

下一章將具體討論這些與索引有關(guān)的優(yōu)化策略烈炭。

兩種引擎索引的比較:

B-/+Tree索引的性能優(yōu)勢: 一般使用磁盤I/O次數(shù)評(píng)價(jià)索引優(yōu)劣溶锭。

1.結(jié)合操作系統(tǒng)存儲(chǔ)結(jié)構(gòu)優(yōu)化處理: mysql巧妙運(yùn)用操作系統(tǒng)存儲(chǔ)結(jié)構(gòu)(一個(gè)節(jié)點(diǎn)分配到一個(gè)存儲(chǔ)頁中->盡量減少IO次數(shù)) & 磁盤預(yù)讀(緩存預(yù)讀->加速預(yù)讀馬上要用到的數(shù)據(jù)).

2.B+Tree 單個(gè)節(jié)點(diǎn)能放多個(gè)子節(jié)點(diǎn),相同IO次數(shù)符隙,檢索出更多信息趴捅。

3.B+TREE 只在葉子節(jié)點(diǎn)存儲(chǔ)數(shù)據(jù) & 所有葉子結(jié)點(diǎn)包含一個(gè)鏈指針 & 其他內(nèi)層非葉子節(jié)點(diǎn)只存儲(chǔ)索引數(shù)據(jù)。只利用索引快速定位數(shù)據(jù)索引范圍霹疫,先定位索引再通過索引高效快速定位數(shù)據(jù)拱绑。

詳解:Mysql設(shè)計(jì)利用了磁盤預(yù)讀原理,將一個(gè)B+Tree節(jié)點(diǎn)大小設(shè)為一個(gè)頁大小更米,在新建節(jié)點(diǎn)時(shí)直接申請(qǐng)一個(gè)頁的空間欺栗,這樣就能保證一個(gè)節(jié)點(diǎn)物理上存儲(chǔ)在一個(gè)頁里,加之計(jì)算機(jī)存儲(chǔ)分配都是按頁對(duì)齊的征峦,這樣就實(shí)現(xiàn)了每個(gè)Node節(jié)點(diǎn)只需要一次I/O操作迟几。

B-Tree索引、B+Tree索引: 單個(gè)節(jié)點(diǎn)能放多個(gè)子節(jié)點(diǎn)栏笆,查詢IO次數(shù)相同(mysql查詢IO次數(shù)最多3-5次-所以需要每個(gè)節(jié)點(diǎn)需要存儲(chǔ)很多數(shù)據(jù))

B+TREE 只在葉子節(jié)點(diǎn)存儲(chǔ)數(shù)據(jù) & 所有葉子結(jié)點(diǎn)包含一個(gè)鏈指針 & 其他內(nèi)層非葉子節(jié)點(diǎn)只存儲(chǔ)索引數(shù)據(jù)类腮。只利用索引快速定位數(shù)據(jù)索引范圍,先定位索引再通過索引高效快速定位數(shù)據(jù)蛉加。

B+Tree更適合外存索引蚜枢,原因和內(nèi)節(jié)點(diǎn)出度d有關(guān)。從上面分析可以看到针饥,d越大索引的性能越好厂抽,而出度的上限取決于節(jié)點(diǎn)內(nèi)key和data的大小:

B+Tree內(nèi)節(jié)點(diǎn)去掉了data域丁眼,因此可以擁有更大的出度筷凤,擁有更好的性能。只利用索引快速定位數(shù)據(jù)索引范圍苞七,先定位索引再通過索引高效快速定位數(shù)據(jù)藐守。

dmax=floor(pagesize/(keysize+datasize+pointsize))


Mysql 索引底層實(shí)現(xiàn)-MyISAM & InnoDB

聚簇索引: 索引 和 數(shù)據(jù)文件為同一個(gè)文件。非聚簇索引: 索引 和 數(shù)據(jù)文件分開的索引蹂风。

MyISAM & InnoDB 都使用B+Tree索引結(jié)構(gòu)卢厂。但是底層索引存儲(chǔ)不同,MyISAM 采用非聚簇索引惠啄,而InnoDB采用聚簇索引慎恒。

MyISAM索引原理:采用非聚簇索引-MyISAM myi索引文件和myd數(shù)據(jù)文件分離任内,索引文件僅保存數(shù)據(jù)記錄的指針地址。葉子節(jié)點(diǎn)data域存儲(chǔ)指向數(shù)據(jù)記錄的指針地址巧号。(底層存儲(chǔ)結(jié)構(gòu): frm -表定義族奢、 myi -myisam索引、 myd-myisam數(shù)據(jù))

MyISAM索引按照B+Tree搜索丹鸿,如果指定的Key存在越走,則取出其data域的值,然后以data域值-數(shù)據(jù)指針地址去讀取相應(yīng)數(shù)據(jù)記錄靠欢。輔助索引和主索引在結(jié)構(gòu)上沒有任何區(qū)別廊敌,只是主索引要求key是唯一的,而輔助索引的key可以重復(fù)门怪。MyISAM索引樹如下:


InnoDB優(yōu)勢:高擴(kuò)展性骡澈,充分發(fā)揮硬件性能、 Crash Safe掷空、 支持事務(wù)肋殴、 可以在線熱備份

InnoDB特性:

  1. 事務(wù)支持(ACID)2. 擴(kuò)展性優(yōu)良 3. 讀寫不沖突 4. 緩存加速

2. 功能組件: redo/undo & 異步IO & MVCC & 行級(jí)別鎖 & Page Cache(LRU)

InnoDB物理存儲(chǔ)結(jié)構(gòu)如下圖:

InnoDB表空間管理

InnoDB物理存儲(chǔ)文件結(jié)構(gòu)說明:

InnoDB以表空間Tablespace(idb文件)結(jié)構(gòu)進(jìn)行組織,每個(gè)Tablespace 包含多個(gè)Segment段坦弟,每個(gè)段(分為2種段:葉子節(jié)點(diǎn)Segment&非葉子節(jié)點(diǎn)Segment), 一個(gè)Segment段包含多個(gè)Extent护锤,一個(gè)Extent占用1M空間包含64個(gè)Page(每個(gè)Page 16k),InnoDB B-Tree 一個(gè)邏輯節(jié)點(diǎn)就分配一個(gè)物理Page,一個(gè)節(jié)點(diǎn)一次IO操作酿傍。,一個(gè)Page里包含很多有序數(shù)據(jù)Row行數(shù)據(jù)烙懦,Row行數(shù)據(jù)中包含F(xiàn)iled屬性數(shù)據(jù)等信息。

? 表空間(ibd文件)

? 段(一個(gè)索引2段:葉子節(jié)點(diǎn)Segment & 非葉子節(jié)點(diǎn)Segment)

? Extent(1MB):一個(gè)Extent(1M) 包含64個(gè) Page(16k),一個(gè)Page里包含很多有序行數(shù)據(jù) , InnoDB B-Tree 一個(gè)邏輯節(jié)點(diǎn)就分配一個(gè)物理Page,一個(gè)節(jié)點(diǎn)一次IO操作赤炒。

? Page(16KB)

? Row

? Field

表插入數(shù)據(jù)擴(kuò)展原理: 一次擴(kuò)張一個(gè)Extent空間(1M)氯析,64個(gè)Page,按照順序結(jié)構(gòu)向每個(gè)page中插入順序。

InnoDB邏輯組織結(jié)構(gòu):

InnoDB索引樹結(jié)構(gòu)

每個(gè)索引一個(gè)B+樹莺褒, 一個(gè)B+樹節(jié)點(diǎn) = 一個(gè)物理Page(16K)

? 數(shù)據(jù)按16KB切片為Page 并編號(hào)掩缓, 編號(hào)可映射到物理文件偏移(16K * N), B+樹葉子節(jié)點(diǎn)前后形成雙向鏈表遵岩, 數(shù)據(jù)按主鍵索引聚簇拾因, 二級(jí)索引葉節(jié)點(diǎn)存儲(chǔ)主鍵值, 通過葉節(jié)點(diǎn)主鍵值回表查找數(shù)據(jù)旷余。

InnoDB索引原理:

采用聚簇索引- InnoDB數(shù)據(jù)&索引文件為一個(gè)idb文件,表數(shù)據(jù)文件本身就是主索引扁达,相鄰的索引臨近存儲(chǔ)正卧。 葉節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄(數(shù)據(jù)[除主鍵id外其他列data]+主索引[索引key:表主鍵id])。 葉子節(jié)點(diǎn)直接存儲(chǔ)數(shù)據(jù)記錄跪解,以主鍵id為key,葉子節(jié)點(diǎn)中直接存儲(chǔ)數(shù)據(jù)記錄炉旷。(底層存儲(chǔ)結(jié)構(gòu): frm -表定義、 ibd: innoDB數(shù)據(jù)&索引文件)

注:由于InnoDB采用聚簇索引結(jié)構(gòu)存儲(chǔ),索引InnoDB的數(shù)據(jù)文件需要按照主鍵聚集窘行,因此InnoDB要求表必須有主鍵(MyISAM可以沒有)饥追。如果沒有指定mysql會(huì)自動(dòng)選擇一個(gè)可以唯一表示數(shù)據(jù)記錄的列作為主鍵,如果不存在這樣的列罐盔,mysql自動(dòng)為InnoDB表生成一個(gè)隱含字段(6個(gè)字節(jié)長整型)作為主鍵但绕。 InnoDB的所有 輔助索引 都引用 數(shù)據(jù)記錄的主鍵 作為data域。

聚簇索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效惶看,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得數(shù)據(jù)記錄主鍵捏顺,然后用主鍵到主索引中檢索獲得數(shù)據(jù)記錄。InnoDB聚簇索引結(jié)構(gòu):

索引查找流程:

1.索引精確查找: 確定定位條件, 找到根節(jié)點(diǎn)Page No, 根節(jié)點(diǎn)讀到內(nèi)存, 逐層向下查找, 讀取葉子節(jié)點(diǎn)Page,通過 二分查找找到記錄或未命中纬黎。(select * from user_info where id = 23)

2.索引范圍查找:讀取根節(jié)點(diǎn)至內(nèi)存, 確定索引定位條件id=18, 找到滿足條件第一個(gè)葉節(jié)點(diǎn)

, 順序掃描所有結(jié)果, 直到終止條件滿足id >=22 (select * from user_info where id >= 18 and id < 22)

3.全表掃描:直接讀取葉節(jié)點(diǎn)頭結(jié)點(diǎn)幅骄, 順序掃描, 返回符合條件記錄本今, 到最終節(jié)點(diǎn)結(jié)束

(select * from user_info where name = 'abc')

4.二級(jí)索引查找

Create table table_x(int id primary key, varchar(64) name,key sec_index(name) )

? Select * from table_x where name = “d”;

通過二級(jí)索引查出對(duì)應(yīng)主鍵拆座,拿主鍵回表查主鍵索引得到數(shù)據(jù), 二級(jí)索引可篩選掉大量無效記錄冠息,提高效率

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挪凑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铐达,更是在濱河造成了極大的恐慌岖赋,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓮孙,死亡現(xiàn)場離奇詭異唐断,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杭抠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門脸甘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人偏灿,你說我怎么就攤上這事丹诀。” “怎么了翁垂?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵铆遭,是天一觀的道長。 經(jīng)常有香客問我沿猜,道長枚荣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任啼肩,我火速辦了婚禮橄妆,結(jié)果婚禮上衙伶,老公的妹妹穿的比我還像新娘。我一直安慰自己害碾,他們只是感情好矢劲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慌随,像睡著了一般芬沉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上儒陨,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天花嘶,我揣著相機(jī)與錄音,去河邊找鬼蹦漠。 笑死椭员,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笛园。 我是一名探鬼主播隘击,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼研铆!你這毒婦竟也來了埋同?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤棵红,失蹤者是張志新(化名)和其女友劉穎凶赁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆甜,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虱肄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了交煞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咏窿。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖素征,靈堂內(nèi)的尸體忽然破棺而出集嵌,到底是詐尸還是另有隱情,我是刑警寧澤御毅,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布根欧,位于F島的核電站,受9級(jí)特大地震影響端蛆,放射性物質(zhì)發(fā)生泄漏咽块。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一欺税、第九天 我趴在偏房一處隱蔽的房頂上張望侈沪。 院中可真熱鬧,春花似錦晚凿、人聲如沸亭罪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽应役。三九已至,卻和暖如春燥筷,著一層夾襖步出監(jiān)牢的瞬間箩祥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工肆氓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袍祖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓谢揪,卻偏偏與公主長得像蕉陋,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拨扶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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