從新學習Mysql數據庫4:Mysql索引實現原理

MySQL索引類型

1、簡介

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

2、語句

<pre>CREATE TABLE table_name[col_name data type]
unique|fulltextindex_name[asc|desc]
</pre>node

1.unique|fulltext爲可選參數,分別表示惟一索引、全文索引
2.index和key爲同義詞,二者做用相同,用來指定建立索引
3.col_name爲須要建立索引的字段列,該列必須從數據表中該定義的多個列中選擇
4.index_name指定索引的名稱,爲可選參數,若是不指定,默認col_name爲索引值
5.length爲可選參數,表示索引的長度,只有字符串類型的字段才能指定索引長度
6.asc或desc指定升序或降序的索引值存儲mysql

3、索引類型

1.普通索引
是最基本的索引,它沒有任何限制。它有如下幾種建立方式:
(1)直接建立索引算法

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

(2)修改表結構的方式添加索引數據庫

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

(3)建立表的時候同時建立索引緩存

複製代碼

<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>架構

複製代碼

(4)刪除索引

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

2.惟一索引
與前面的普通索引相似,不一樣的就是:索引列的值必須惟一,但容許有空值。若是是組合索引,則列值的組合必須惟一。它有如下幾種建立方式:
(1)建立惟一索引

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

(2)修改表結構

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

(3)建立表的時候直接指定

複製代碼

<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>

複製代碼

3.主鍵索引
是一種特殊的惟一索引,一個表只能有一個主鍵,不容許有空值。通常是在建表的時候同時建立主鍵索引:

<pre>CREATE TABLE table (

`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) NOT NULL ,
PRIMARY KEY (`id`)

);
</pre>

4.組合索引
指多個字段上建立的索引,只有在查詢條件中使用了建立索引時的第一個字段,索引纔會被使用。使用組合索引時遵循最左前綴集合

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

5.全文索引
主要用來查找文本中的關鍵字,而不是直接與索引中的值相比較。fulltext索引跟其它索引大不相同,它更像是一個搜索引擎,而不是簡單的where語句的參數匹配。fulltext索引配合match against操做使用,而不是通常的where語句加like。它能夠在create table,alter table ,create index使用,不過目前只有char、varchar,text 列上能夠建立全文索引。值得一提的是,在數據量較大時候,現將數據放入一個沒有全局索引的表中,而後再用CREATE index建立fulltext索引,要比先爲一張表創建fulltext而後再將數據寫入的速度快不少。
(1)建立表的適合添加全文索引

複製代碼

<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>

複製代碼

(2)修改表結構添加全文索引

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

(3)直接建立索引

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

4、缺點

1.雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行insert、update和delete。由於更新表時,不只要保存數據,還要保存一下索引文件。
2.創建索引會佔用磁盤空間的索引文件。通常狀況這個問題不太嚴重,但若是你在一個大表上建立了多種組合索引,索引文件的會增加很快。
索引只是提升效率的一個因素,若是有大數據量的表,就須要花時間研究創建最優秀的索引,或優化查詢語句。

5、注意事項

使用索引時,有如下一些技巧和注意事項:
1.索引不會包含有null值的列
只要列中包含有null值都將不會被包含在索引中,複合索引中只要有一列含有null值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲null。
2.使用短索引
對串列進行索引,若是可能應該指定一個前綴長度。例如,若是有一個char(255)的列,若是在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不只能夠提升查詢速度並且能夠節省磁盤空間和I/O操做。
3.索引列排序
查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。
4.like語句操做
通常狀況下不推薦使用like操做,若是非使用不可,如何使用也是一個問題。like 「%aaa%」 不會使用索引而like 「aaa%」可使用索引。
5.不要在列上進行運算
這將致使索引失效而進行全表掃描,例如

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

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

參考http://blog.codinglabs.org/ar...

摘要

本文以MySQL數據庫爲研究對象,討論與數據庫索引相關的一些話題。特別須要說明的是,MySQL支持諸多存儲引擎,而各類存儲引擎對索引的支持也各不相同,所以MySQL數據庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等。爲了不混亂,本文將只關注於BTree索引,由於這是日常使用MySQL時主要打交道的索引,至於哈希索引和全文索引本文暫不討論。

文章主要內容分爲三個部分。

第一部分主要從數據結構及算法理論層面討論MySQL數據庫索引的數理基礎。

第二部分結合MySQL數據庫中MyISAM和InnoDB數據存儲引擎中索引的架構實現討論彙集索引、非彙集索引及覆蓋索引等話題。

第三部分根據上面的理論基礎,討論MySQL中高性能使用索引的策略。

數據結構及算法基礎

索引的本質

MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就能夠獲得索引的本質:索引是數據結構。

咱們知道,數據庫查詢是數據庫的最主要功能之一。咱們都但願查詢數據的速度能儘量的快,所以數據庫系統的設計者會從查詢算法的角度進行優化。最基本的查詢算法固然是順序查找(linear search),這種複雜度爲O(n)的算法在數據量很大時顯然是糟糕的,好在計算機科學的發展提供了不少更優秀的查找算法,例如二分查找(binary search)、二叉樹查找(binary tree search)等。若是稍微分析一下會發現,每種查找算法都只能應用於特定的數據結構之上,例如二分查找要求被檢索數據有序,而二叉樹查找只能應用於二叉查找樹上,可是數據自己的組織結構不可能徹底知足各類數據結構(例如,理論上不可能同時將兩列都按順序進行組織),因此,在數據以外,數據庫系統還維護着知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就能夠在這些數據結構上實現高級查找算法。這種數據結構,就是索引。

看一個例子:

圖1

圖1展現了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也並非必定物理相鄰的)。爲了加快Col2的查找,能夠維護一個右邊所示的二叉查找樹,每一個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就能夠運用二叉查找在O(log2n)O(log2n)的複雜度內獲取到相應數據。

雖然這是一個貨真價實的索引,可是實際的數據庫系統幾乎沒有使用二叉查找樹或其進化品種紅黑樹(red-black tree)實現的,緣由會在下文介紹。

B-Tree和B+Tree

目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構,在本文的下一節會結合存儲器原理及計算機存取原理討論爲何B-Tree和B+Tree在被如此普遍用於索引,這一節先單純從數據結構角度描述它們。

B-Tree

爲了描述B-Tree,首先定義一條數據記錄爲一個二元組[key, data],key爲記錄的鍵值,對於不一樣數據記錄,key是互不相同的;data爲數據記錄除key外的數據。那麼B-Tree是知足下列條件的數據結構:

d爲大於1的一個正整數,稱爲B-Tree的度。

h爲一個正整數,稱爲B-Tree的高度。

每一個非葉子節點由n-1個key和n個指針組成,其中d<=n<=2d。

每一個葉子節點最少包含一個key和兩個指針,最多包含2d-1個key和2d個指針,葉節點的指針均爲null 。

全部葉節點具備相同的深度,等於樹高h。

key和指針互相間隔,節點兩端是指針。

一個節點中的key從左到右非遞減排列。

全部節點組成樹結構。

每一個指針要麼爲null,要麼指向另一個節點。

若是某個指針在節點node最左邊且不爲null,則其指向節點的全部key小於v(key1)v(key1),其中v(key1)v(key1)爲node的第一個key的值。

若是某個指針在節點node最右邊且不爲null,則其指向節點的全部key大於v(keym)v(keym),其中v(keym)v(keym)爲node的最後一個key的值。

若是某個指針在節點node的左右相鄰key分別是keyikeyi和keyi+1keyi+1且不爲null,則其指向節點的全部key小於v(keyi+1)v(keyi+1)且大於v(keyi)v(keyi)。

圖2是一個d=2的B-Tree示意圖。

圖2

因爲B-Tree的特性,在B-Tree中按key檢索數據的算法很是直觀:首先從根節點進行二分查找,若是找到則返回對應節點的data,不然對相應區間的指針指向的節點遞歸進行查找,直到找到節點或找到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);

關於B-Tree有一系列有趣的性質,例如一個度爲d的B-Tree,設其索引N個key,則其樹高h的上限爲logd((N+1)/2)logd((N+1)/2),檢索一個key,其查找節點個數的漸進複雜度爲O(logdN)O(logdN)。從這點能夠看出,B-Tree是一個很是有效率的索引數據結構。

另外,因爲插入刪除新的數據記錄會破壞B-Tree的性質,所以在插入刪除時,須要對樹進行一個分裂、合併、轉移等操做以保持B-Tree性質,本文不打算完整討論B-Tree這些內容,由於已經有許多資料詳細說明了B-Tree的數學性質及插入刪除算法,有興趣的朋友能夠在本文末的參考文獻一欄找到相應的資料進行閱讀。

B+Tree

B-Tree有許多變種,其中最多見的是B+Tree,例如MySQL就廣泛使用B+Tree實現其索引結構。

與B-Tree相比,B+Tree有如下不一樣點:

每一個節點的指針上限爲2d而不是2d+1。

內節點不存儲data,只存儲key;葉子節點不存儲指針。

圖3是一個簡單的B+Tree示意。

圖3

因爲並非全部節點都具備相同的域,所以B+Tree中葉節點和內節點通常大小不一樣。這點與B-Tree不一樣,雖然B-Tree中不一樣節點存放的key和指針可能數量不一致,可是每一個節點的域和上限是一致的,因此在實現中B-Tree每每對每一個節點申請同等大小的空間。

通常來講,B+Tree比B-Tree更適合實現外存儲索引結構,具體緣由與外存儲器原理及計算機存取原理有關,將在下面討論。

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

通常在數據庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增長了順序訪問指針。

圖4

如圖4所示,在B+Tree的每一個葉子節點增長一個指向相鄰葉子節點的指針,就造成了帶有順序訪問指針的B+Tree。作這個優化的目的是爲了提升區間訪問的性能,例如圖4中若是要查詢key爲從18到49的全部數據記錄,當找到18後,只需順着節點和指針順序遍歷就能夠一次性訪問到全部數據節點,極大提到了區間查詢效率。

這一節對B-Tree和B+Tree進行了一個簡單的介紹,下一節結合存儲器存取原理介紹爲何目前B+Tree是數據庫系統實現索引的首選數據結構。

爲何使用B-Tree(B+Tree)

上文說過,紅黑樹等數據結構也能夠用來實現索引,可是文件系統及數據庫系統廣泛採用B-/+Tree做爲索引結構,這一節將結合計算機組成原理相關知識討論B-/+Tree做爲索引的理論基礎。

通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程當中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,因此評價一個數據結構做爲索引的優劣最重要的指標就是在查找過程當中磁盤I/O操做次數的漸進複雜度。換句話說,索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數。下面先介紹內存和磁盤存取原理,而後再結合這些原理分析B-/+Tree做爲索引的效率。

B樹

爲何要B樹

磁盤中有兩個機械運動的部分,分別是盤片旋轉和磁臂移動。盤片旋轉就是咱們市面上所提到的多少轉每分鐘,而磁盤移動則是在盤片旋轉到指定位置之後,移動磁臂後開始進行數據的讀寫。那麼這就存在一個定位到磁盤中的塊的過程,而定位是磁盤的存取中花費時間比較大的一塊,畢竟機械運動花費的時候要遠遠大於電子運動的時間。當大規模數據存儲到磁盤中的時候,顯然定位是一個很是花費時間的過程,可是咱們能夠經過B樹進行優化,提升磁盤讀取時定位的效率。

爲何B類樹能夠進行優化呢?咱們能夠根據B類樹的特色,構造一個多階的B類樹,而後在儘可能多的在結點上存儲相關的信息,保證層數儘可能的少,以便後面咱們能夠更快的找到信息,磁盤的I/O操做也少一些,並且B類樹是平衡樹,每一個結點到葉子結點的高度都是相同,這也保證了每一個查詢是穩定的。

簡介

這裏的B樹,也就是英文中的B-Tree,一個 m 階的B樹知足如下條件:

  1. 每一個結點至多擁有m棵子樹;
  2. 根結點至少擁有兩顆子樹(存在子樹的狀況下);
  3. 除了根結點之外,其他每一個分支結點至少擁有 m/2 棵子樹;
  4. 全部的葉結點都在同一層上;
  5. 有 k 棵子樹的分支結點則存在 k-1 個關鍵碼,關鍵碼按照遞增次序進行排列;
  6. 關鍵字數量須要知足ceil(m/2)-1 <= n <= m-1;

舉個栗子:

B樹上大部分的操做所須要的磁盤存取次數和B樹的高度是成正比的,在B樹中能夠檢查多個子結點,因爲在一棵樹中檢查任意一個結點都須要一次磁盤訪問,因此B樹避免了大量的磁盤訪問。

操做

既然是樹,那麼必不可少的操做就是插入和刪除,這也是B樹和其它數據結構不一樣的地方,固然了,還有必不可少的搜索,分享一個對B樹的操做進行可視化的網址,它是由usfca提供的。

假定對高度爲h的m階B樹進行操做。

插入

新結點通常插在第h層,經過搜索找到對應的結點進行插入,那麼根據即將插入的結點的數量又分爲下面幾種狀況。

  • 若是該結點的關鍵字個數沒有到達m-1個,那麼直接插入便可;
  • 若是該結點的關鍵字個數已經到達了m-1個,那麼根據B樹的性質顯然沒法知足,須要將其進行分裂。分裂的規則是該結點分紅兩半,將中間的關鍵字進行提高,加入到父親結點中,可是這又可能存在父親結點也滿員的狀況,則不得不向上進行回溯,甚至是要對根結點進行分裂,那麼整棵樹都加了一層。

其過程以下:

刪除

一樣的,咱們須要先經過搜索找到相應的值,存在則進行刪除,須要考慮刪除之後的狀況,

  • 若是該結點擁有關鍵字數量仍然知足B樹性質,則不作任何處理;
  • 若是該結點在刪除關鍵字之後不知足B樹的性質(關鍵字沒有到達ceil(m/2)-1的數量),則須要向兄弟結點借關鍵字,這有分爲兄弟結點的關鍵字數量是否足夠的狀況。

    • 若是兄弟結點的關鍵字足夠借給該結點,則過程爲將父親結點的關鍵字下移,兄弟結點的關鍵字上移;
    • 若是兄弟結點的關鍵字在借出去之後也沒法知足狀況,即以前兄弟結點的關鍵字的數量爲ceil(m/2)-1,借的一方的關鍵字數量爲ceil(m/2)-2的狀況,那麼咱們能夠將該結點合併到兄弟結點中,合併以後的子結點數量少了一個,則須要將父親結點的關鍵字下放,若是父親結點不知足性質,則向上回溯;
  • 其他狀況參照BST中的刪除。

其過程以下:

B+樹

爲何要B+樹

因爲B+樹的數據都存儲在葉子結點中,分支結點均爲索引,方便掃庫,只須要掃一遍葉子結點便可,可是B樹由於其分支結點一樣存儲着數據,咱們要找到具體的數據,須要進行一次中序遍歷按序來掃,因此B+樹更加適合在區間查詢的狀況,因此一般B+樹用於數據庫索引,而B樹則經常使用於文件索引。

簡介

一樣的,以一個m階樹爲例:

  1. 根結點只有一個,分支數量範圍爲[2,m];
  2. 分支結點,每一個結點包含分支數範圍爲[ceil(m/2), m];
  3. 分支結點的關鍵字數量等於其子分支的數量減一,關鍵字的數量範圍爲[ceil(m/2)-1, m-1],關鍵字順序遞增;
  4. 全部葉子結點都在同一層;

操做

其操做和B樹的操做是相似的,不過須要注意的是,在增長值的時候,若是存在滿員的狀況,將選擇結點中的值做爲新的索引,還有在刪除值的時候,索引中的關鍵字並不會刪除,也不會存在父親結點的關鍵字下沉的狀況,由於那只是索引。

B樹和B+樹的區別

這都是因爲B+樹和B具備這不一樣的存儲結構所形成的區別,以一個m階樹爲例。

  1. 關鍵字的數量不一樣;B+樹中分支結點有m個關鍵字,其葉子結點也有m個,其關鍵字只是起到了一個索引的做用,可是B樹雖然也有m個子結點,可是其只擁有m-1個關鍵字。
  2. 存儲的位置不一樣;B+樹中的數據都存儲在葉子結點上,也就是其全部葉子結點的數據組合起來就是完整的數據,可是B樹的數據存儲在每個結點中,並不只僅存儲在葉子結點上。
  3. 分支結點的構造不一樣;B+樹的分支結點僅僅存儲着關鍵字信息和兒子的指針(這裏的指針指的是磁盤塊的偏移量),也就是說內部結點僅僅包含着索引信息。
  4. 查詢不一樣;B樹在找到具體的數值之後,則結束,而B+樹則須要經過索引找到葉子結點中的數據才結束,也就是說B+樹的搜索過程當中走了一條從根結點到葉子結點的路徑。

主存存取原理

目前計算機使用的主存基本都是隨機讀寫存儲器(RAM),現代RAM的結構和存取原理比較複雜,這裏本文拋卻具體差異,抽象出一個十分簡單的存取模型來講明RAM的工做原理。

圖5

從抽象角度看,主存是一系列的存儲單元組成的矩陣,每一個存儲單元存儲固定大小的數據。每一個存儲單元有惟一的地址,現代主存的編址規則比較複雜,這裏將其簡化成一個二維地址:經過一個行地址和一個列地址能夠惟必定位到一個存儲單元。圖5展現了一個4 x 4的主存模型。

主存的存取過程以下:

當系統須要讀取主存時,則將地址信號放到地址總線上傳給主存,主存讀到地址信號後,解析信號並定位到指定存儲單元,而後將此存儲單元數據放到數據總線上,供其它部件讀取。

寫主存的過程相似,系統將要寫入單元地址和數據分別放在地址總線和數據總線上,主存讀取兩個總線的內容,作相應的寫操做。

這裏能夠看出,主存存取的時間僅與存取次數呈線性關係,由於不存在機械操做,兩次存取的數據的「距離」不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是同樣的。

磁盤存取原理

上文說過,索引通常以文件形式存儲在磁盤上,索引檢索須要磁盤I/O操做。與主存不一樣,磁盤I/O存在機械運動耗費,所以磁盤I/O的時間消耗是巨大的。

圖6是磁盤的總體結構示意圖。

圖6

一個磁盤由大小相同且同軸的圓形盤片組成,磁盤能夠轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每一個磁頭負責存取一個磁盤的內容。磁頭不能轉動,可是能夠沿磁盤半徑方向運動(實際是斜切向運動),每一個磁頭同一時刻也必須是同軸的,即從正上方向下看,全部磁頭任什麼時候候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。

圖7是磁盤結構的示意圖。

圖7

盤片被劃分紅一系列同心環,圓心是盤片中心,每一個同心環叫作一個磁道,全部半徑相同的磁道組成一個柱面。磁道被沿半徑線劃分紅一個個小的段,每一個段叫作一個扇區,每一個扇區是磁盤的最小存儲單元。爲了簡單起見,咱們下面假設磁盤只有一個盤片和一個磁頭。

當須要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即肯定要讀的數據在哪一個磁道,哪一個扇區。爲了讀取這個扇區的數據,須要將磁頭放到這個扇區上方,爲了實現這一點,磁頭須要移動對準相應磁道,這個過程叫作尋道,所耗費時間叫作尋道時間,而後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫作旋轉時間。

局部性原理與磁盤預讀

因爲存儲介質的特性,磁盤自己存取就比主存慢不少,再加上機械運動耗費,磁盤的存取速度每每是主存的幾百分分之一,所以爲了提升效率,要儘可能減小磁盤I/O。爲了達到這個目的,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存。這樣作的理論依據是計算機科學中著名的局部性原理:

當一個數據被用到時,其附近的數據也一般會立刻被使用。

程序運行期間所須要的數據一般比較集中。

因爲磁盤順序讀取的效率很高(不須要尋道時間,只需不多的旋轉時間),所以對於具備局部性的程序來講,預讀能夠提升I/O效率。

預讀的長度通常爲頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。

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

到這裏終於能夠分析B-/+Tree索引的性能了。

上文說過通常使用磁盤I/O次數評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多須要訪問h個節點。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入。爲了達到這個目的,在實際實現B-Tree還須要使用以下技巧:

每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。

B-Tree中一次檢索最多須要h-1次I/O(根節點常駐內存),漸進複雜度爲O(h)=O(logdN)O(h)=O(logdN)。通常實際應用中,出度d是很是大的數字,一般超過100,所以h很是小(一般不超過3)。

綜上所述,用B-Tree做爲索引結構效率是很是高的。

而紅黑樹這種結構,h明顯要深的多。因爲邏輯上很近的節點(父子)物理上可能很遠,沒法利用局部性,因此紅黑樹的I/O漸進複雜度也爲O(h),效率明顯比B-Tree差不少。

上文還說過,B+Tree更適合外存索引,緣由和內節點出度d有關。從上面分析能夠看到,d越大索引的性能越好,而出度的上限取決於節點內key和data的大小:

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

floor表示向下取整。因爲B+Tree內節點去掉了data域,所以能夠擁有更大的出度,擁有更好的性能。

這一章從理論角度討論了與索引相關的數據結構與算法問題,下一章將討論B+Tree是如何具體實現爲MySQL中索引,同時將結合MyISAM和InnDB存儲引擎介紹非彙集索引和彙集索引兩種不一樣的索引實現形式。

MySQL索引實現

在MySQL中,索引屬於存儲引擎級別的概念,不一樣存儲引擎對索引的實現方式是不一樣的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。

MyISAM索引實現

MyISAM引擎使用B+Tree做爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:

圖8

這裏設表一共有三列,假設咱們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複。若是咱們在Col2上創建一個輔助索引,則此索引的結構以下圖所示:

圖9

一樣也是一顆B+Tree,data域保存數據記錄的地址。所以,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後以data域的值爲地址,讀取相應數據記錄。

MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。

InnoDB索引實現

雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。

第一個重大區別是InnoDB的數據文件自己就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件自己就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。

圖10

圖10是InnoDB主索引(同時也是數據文件)的示意圖,能夠看到葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有),若是沒有顯式指定,則MySQL系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。

第二個與MyISAM索引的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。例如,圖11爲定義在Col3上的一個輔助索引:

圖11

這裏以英文字符的ASCII碼做爲比較準則。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。

瞭解不一樣存儲引擎的索引實現方式對於正確使用和優化索引都很是有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲何不建議使用過長的字段做爲主鍵,由於全部輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段做爲主鍵在InnoDB中不是個好主意,由於InnoDB數據文件自己是一顆B+Tree,非單調的主鍵會形成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段做爲主鍵則是一個很好的選擇。

下一章將具體討論這些與索引有關的優化策略。

兩種引擎索引的比較:

B-/+Tree索引的性能優點: 通常使用磁盤I/O次數評價索引優劣。

1.結合操做系統存儲結構優化處理: mysql巧妙運用操做系統存儲結構(一個節點分配到一個存儲頁中->儘可能減小IO次數) & 磁盤預讀(緩存預讀->加速預讀立刻要用到的數據).

2.B+Tree 單個節點能放多個子節點,相同IO次數,檢索出更多信息。

3.B+TREE 只在葉子節點存儲數據 & 全部葉子結點包含一個鏈指針 & 其餘內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引範圍,先定位索引再經過索引高效快速定位數據。

詳解:Mysql設計利用了磁盤預讀原理,將一個B+Tree節點大小設爲一個頁大小,在新建節點時直接申請一個頁的空間,這樣就能保證一個節點物理上存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,這樣就實現了每一個Node節點只須要一次I/O操做。

B-Tree索引、B+Tree索引: 單個節點能放多個子節點,查詢IO次數相同(mysql查詢IO次數最多3-5次-因此須要每一個節點須要存儲不少數據)

B+TREE 只在葉子節點存儲數據 & 全部葉子結點包含一個鏈指針 & 其餘內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引範圍,先定位索引再經過索引高效快速定位數據。

B+Tree更適合外存索引,緣由和內節點出度d有關。從上面分析能夠看到,d越大索引的性能越好,而出度的上限取決於節點內key和data的大小:

B+Tree內節點去掉了data域,所以能夠擁有更大的出度,擁有更好的性能。只利用索引快速定位數據索引範圍,先定位索引再經過索引高效快速定位數據。

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


Mysql 索引底層實現-MyISAM & InnoDB

聚簇索引: 索引 和 數據文件爲同一個文件。非聚簇索引: 索引 和 數據文件分開的索引。

MyISAM & InnoDB 都使用B+Tree索引結構。可是底層索引存儲不一樣,MyISAM 採用非聚簇索引,而InnoDB採用聚簇索引。 

MyISAM索引原理:採用非聚簇索引-MyISAM myi索引文件和myd數據文件分離,索引文件僅保存數據記錄的指針地址。葉子節點data域存儲指向數據記錄的指針地址。(底層存儲結構: frm -表定義、 myi -myisam索引、 myd-myisam數據)

MyISAM索引按照B+Tree搜索,若是指定的Key存在,則取出其data域的值,而後以data域值-數據指針地址去讀取相應數據記錄。輔助索引和主索引在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複。MyISAM索引樹以下:


InnoDB優點:高擴展性,充分發揮硬件性能、 Crash Safe、 支持事務、 能夠在線熱備份

InnoDB特性:

1. 事務支持(ACID)2. 擴展性優良 3. 讀寫不衝突 4. 緩存加速

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

InnoDB物理存儲結構以下圖:

InnoDB表空間管理

InnoDB物理存儲文件結構說明:

    InnoDB以表空間Tablespace(idb文件)結構進行組織,每一個Tablespace 包含多個Segment段,每一個段(分爲2種段:葉子節點Segment&非葉子節點Segment), 一個Segment段包含多個Extent,一個Extent佔用1M空間包含64個Page(每一個Page 16k),InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操做。,一個Page裏包含不少有序數據Row行數據,Row行數據中包含Filed屬性數據等信息。

• 表空間(ibd文件)

• 段(一個索引2段:葉子節點Segment & 非葉子節點Segment)

• Extent(1MB):一個Extent(1M) 包含64個 Page(16k),一個Page裏包含不少有序行數據 , InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操做。

• Page(16KB)

• Row

• Field

表插入數據擴展原理: 一次擴張一個Extent空間(1M),64個Page,按照順序結構向每一個page中插入順序。

InnoDB邏輯組織結構:

InnoDB索引樹結構

每一個索引一個B+樹, 一個B+樹節點 = 一個物理Page(16K)

• 數據按16KB切片爲Page 並編號, 編號可映射到物理文件偏移(16K * N), B+樹葉子節點先後造成雙向鏈表, 數據按主鍵索引聚簇, 二級索引葉節點存儲主鍵值, 經過葉節點主鍵值回表查找數據。

InnoDB索引原理: 

  採用聚簇索引- InnoDB數據&索引文件爲一個idb文件,表數據文件自己就是主索引,相鄰的索引臨近存儲。 葉節點data域保存了完整的數據記錄(數據[除主鍵id外其餘列data]+主索引[索引key:表主鍵id])。 葉子節點直接存儲數據記錄,以主鍵id爲key,葉子節點中直接存儲數據記錄。(底層存儲結構: frm -表定義、 ibd: innoDB數據&索引文件)

注:因爲InnoDB採用聚簇索引結構存儲,索引InnoDB的數據文件須要按照主鍵彙集,所以InnoDB要求表必須有主鍵(MyISAM能夠沒有)。若是沒有指定mysql會自動選擇一個能夠惟一表示數據記錄的列做爲主鍵,若是不存在這樣的列,mysql自動爲InnoDB表生成一個隱含字段(6個字節長整型)做爲主鍵。 InnoDB的全部 輔助索引 都引用 數據記錄的主鍵 做爲data域。

  聚簇索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到數據記錄主鍵,而後用主鍵到主索引中檢索得到數據記錄。InnoDB聚簇索引結構:

索引查找流程:

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

2.索引範圍查找:讀取根節點至內存, 肯定索引定位條件id=18, 找到知足條件第一個葉節點

, 順序掃描全部結果, 直到終止條件知足id >=22 (select * from user_info where id >= 18 and id < 22)

3.全表掃描:直接讀取葉節點頭結點, 順序掃描, 返回符合條件記錄, 到最終節點結束

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

4.二級索引查找

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

• Select * from table_x where name = 「d」;

經過二級索引查出對應主鍵,拿主鍵回表查主鍵索引獲得數據, 二級索引可篩選掉大量無效記錄,提升效率

相關文章
相關標籤/搜索