1. 程式人生 > >【搞定MySQL資料庫】:MySQL索引實現原理

【搞定MySQL資料庫】:MySQL索引實現原理

本文轉發自:https://blog.csdn.net/a724888/article/details/78366383

本文主要轉載自幾篇關於MySQL資料庫索引相關的文章。可以相互參考著看。

目錄

1、MySQL索引型別

1.1、簡介

1.2、語句

1.3、索引型別

1.4、索引的缺點

1.5、使用索引時的注意事項

2、MySQL索引背後的資料結構及演算法原理

2.1、摘要

2.2、資料結構及演算法基礎

2.2.1、索引的本質

2.2.2、B-Tree 和 B+Tree

2.2.3、帶有順序訪問指標的B+Tree

2.3、為什麼使用B-Tree(B+Tree)

2.3.1、主存存取原理

2.3.2、磁碟存取原理

2.3.3、區域性性原理與磁碟預讀

2.3.4、B-/+Tree索引的效能分析

3、MySQL索引實現

3.1、MyISAM索引實現

3.2、InnoDB索引實現

3.3、兩種引擎索引的比較

3.4、MySQL 索引底層實現-MyISAM & InnoDB

4、索引查詢流程


1、MySQL索引型別

1.1、簡介

MySQL目前主要有以下幾種索引型別:普通索引、唯一索引、主鍵索引、組合索引、全文索引。

1.2、語句

CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key][index_name](col_name[length])[asc|desc];

1.unique|fulltext 為可選引數,分別表示唯一索引、全文索引;

2.index 和 key 為同義詞,兩者作用相同,用來指定建立索引;

3.index_name 指定索引的名稱,為可選引數,如果不指定,預設col_name為索引值;

4.col_name 為需要建立索引的欄位列,該列必須從資料表中該定義的多個列中選擇;

5.length 為可選引數,表示索引的長度,只有字串型別的欄位才能指定索引長度;

6.asc 或 desc指定升序或降序的索引值儲存。

1.3、索引型別

  • 1.普通索引

是最基本的索引,它沒有任何限制。它有以下幾種建立方式:

(1)直接建立索引

CREATE INDEX index_name ON table(column(length));

(2)修改表結構的方式新增索引

ALTER TABLE table_name ADD INDEX index_name ON (column(length));

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

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

(4)刪除索引

DROP INDEX index_name ON table;
  • 2.唯一索引

與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種建立方式:

(1)建立唯一索引

CREATE UNIQUE INDEX indexName ON table(column(length));

(2)修改表結構

ALTER TABLE table_name ADD UNIQUE indexName ON (column(length));

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

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))
);
  • 3.主鍵索引

是一種特殊的唯一索引,一個表只能有一個主鍵,不允許有空值。一般是在建表的時候同時建立主鍵索引:

CREATE TABLE `table` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` char(255) NOT NULL ,
    PRIMARY KEY (`id`)
);
  • 4.組合索引

指多個欄位上建立的索引,只有在查詢條件中使用了建立索引時的第一個欄位,索引才會被使用。使用組合索引時遵循最左字首集合

ALTER TABLE `table` ADD INDEX name_city_age (name,city,age); 
  • 5.全文索引

主要用來查詢文字中的關鍵字,而不是直接與索引中的值相比較。fulltext 索引跟其它索引大不相同,它更像是一個搜尋引擎,而不是簡單的 where 語句的引數匹配。fulltext 索引配合 match against 操作使用,而不是一般的where語句加like。它可以在create table,alter table ,create index使用,不過目前只有char、varchar,text 列上可以建立全文索引。值得一提的是,在資料量較大時候,先將資料放入一個沒有全域性索引的表中,然後再用CREATE index建立 fulltext 索引,要比先為一張表建立 fulltext 然後再將資料寫入的速度快很多。

(1)建立表的適合新增全文索引

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

(2)修改表結構新增全文索引

ALTER TABLE article ADD FULLTEXT index_content(content);

(3)直接建立索引

CREATE FULLTEXT INDEX index_content ON article(content);

1.4、索引的缺點

1、雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行insert、update和delete。因為更新表時,不僅要儲存資料,還要儲存一下索引檔案。

2、建立索引會佔用磁碟空間的索引檔案。一般情況這個問題不太嚴重,但如果你在一個大表上建立了多種組合索引,索引檔案的會增長很快。

索引只是提高效率的一個因素,如果有大資料量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。

1.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.不要在列上進行運算

這將導致索引失效而進行全表掃描,例如

SELECT * FROM table_name WHERE YEAR(column_name)<2017;
  • 6.不使用not in和<>操作

2、MySQL索引背後的資料結構及演算法原理

2.1、摘要

本文以MySQL資料庫為研究物件,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支援諸多儲存引擎,而各種儲存引擎對索引的支援也各不相同,因此MySQL資料庫支援多種索引型別,如BTree索引,雜湊索引,全文索引等等。為了避免混亂,本文將只關注於BTree索引,因為這是平常使用MySQL時主要打交道的索引,至於雜湊索引和全文索引本文暫不討論。

該節主要內容分為三個部分。

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

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

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

2.2、資料結構及演算法基礎

2.2.1、索引的本質

MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取資料的資料結構。提取句子主幹,就可以得到索引的本質:索引是資料結構

我們知道,資料庫查詢是資料庫的最主要功能之一。我們都希望查詢資料的速度能儘可能的快,因此資料庫系統的設計者會從查詢演算法的角度進行優化。最基本的查詢演算法當然是順序查詢(linear search),這種複雜度為O(n)的演算法在資料量很大時顯然是糟糕的,好在電腦科學的發展提供了很多更優秀的查詢演算法,例如二分查詢(binary search)、二叉樹查詢(binary tree search)等。如果稍微分析一下會發現,每種查詢演算法都只能應用於特定的資料結構之上,例如二分查詢要求被檢索資料有序,而二叉樹查詢只能應用於二叉查詢樹上,但是資料本身的組織結構不可能完全滿足各種資料結構(例如,理論上不可能同時將兩列都按順序進行組織),所以,在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,這樣就可以在這些資料結構上實現高階查詢演算法。這種資料結構,就是索引。

看一個例子:

圖1展示了一種可能的索引方式。左邊是資料表,一共有兩列七條記錄,最左邊的是資料記錄的實體地址(注意邏輯上相鄰的記錄在磁碟上也並不是一定物理相鄰的)。為了加快Col2的查詢,可以維護一個右邊所示的二叉查詢樹,每個節點分別包含索引鍵值和一個指向對應資料記錄實體地址的指標,這樣就可以運用二叉查詢在O(\log_{2}n)的複雜度內獲取到相應資料。

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

2.2.2、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)為node的第一個key的值。
  • 如果某個指標在節點node最右邊且不為null,則其指向節點的所有key大於v(keym),其中v(keym)為node的最後一個key的值。
  • 如果某個指標在節點node的左右相鄰key分別是keyi和keyi+1且不為null,則其指向節點的所有key小於v(keyi+1)且大於v(keyi)。

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

由於B-Tree的特性,在B-Tree中按key檢索資料的演算法非常直觀:首先從根節點進行二分查詢,如果找到則返回對應節點的data,否則對相應區間的指標指向的節點遞迴進行查詢,直到找到節點或找到null指標,前者查詢成功,後者查詢失敗。B-Tree上查詢演算法的虛擬碼如下:

BTree_Search(node, key) {
    if(node == null) return null;
    foreach(node.key)
    {
        if(node.key[i] == key) return node.data[i];
            if(node.key[i] > key) return BTree_Search(point[i]->node);
    }
    return BTree_Search(point[i+1]->node);
}
data = BTree_Search(root, my_key);

關於B-Tree有一系列有趣的性質,例如一個度為d的B-Tree,設其索引N個key,則其樹高h的上限為 logd((N+1)/2),檢索一個key,其查詢節點個數的漸進複雜度為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示意。

由於並不是所有節點都具有相同的域,因此B+Tree中葉節點和內節點一般大小不同。這點與B-Tree不同,雖然B-Tree中不同節點存放的key和指標可能數量不一致,但是每個節點的域和上限是一致的,所以在實現中B-Tree往往對每個節點申請同等大小的空間。

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

2.2.3、帶有順序訪問指標的B+Tree

一般在資料庫系統或檔案系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增加了順序訪問指標

如上圖所示,在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指標,就形成了帶有順序訪問指標的B+Tree。做這個優化的目的是為了提高區間訪問的效能,例如圖中如果要查詢key為從18到49的所有資料記錄,當找到18後,只需順著節點和指標順序遍歷就可以一次性訪問到所有資料節點,極大提到了區間查詢效率。

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

2.3、為什麼使用B-Tree(B+Tree)

上文說過,紅黑樹等資料結構也可以用來實現索引,但是檔案系統及資料庫系統普遍採用B-/+Tree作為索引結構,這一節將結合計算機組成原理相關知識討論B-/+Tree作為索引的理論基礎。

一般來說,索引本身也很大,不可能全部儲存在記憶體中,因此索引往往以索引檔案的形式儲存的磁碟上。這樣的話,索引查詢過程中就要產生磁碟I/O消耗,相對於記憶體存取,I/O存取的消耗要高几個數量級,所以評價一個數據結構作為索引的優劣最重要的指標就是在查詢過程中磁碟I/O操作次數的漸進複雜度換句話說,索引的結構組織要儘量減少查詢過程中磁碟I/O的存取次數。下面先介紹記憶體和磁碟存取原理,然後再結合這些原理分析B-/+Tree作為索引的效率。

2.3.1、主存存取原理

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

從抽象角度看,主存是一系列的儲存單元組成的矩陣,每個儲存單元儲存固定大小的資料。每個儲存單元有唯一的地址,現代主存的編址規則比較複雜,這裡將其簡化成一個二維地址:通過一個行地址和一個列地址可以唯一定位到一個儲存單元。圖5展示了一個4 x 4的主存模型。

主存的存取過程如下:

當系統需要讀取主存時,則將地址訊號放到地址總線上傳給主存,主存讀到地址訊號後,解析訊號並定位到指定儲存單元,然後將此儲存單元資料放到資料匯流排上,供其它部件讀取。

寫主存的過程類似,系統將要寫入單元地址和資料分別放在地址匯流排和資料匯流排上,主存讀取兩個匯流排的內容,做相應的寫操作。

這裡可以看出,主存存取的時間僅與存取次數呈線性關係,因為不存在機械操作,兩次存取的資料的“距離”不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是一樣的。

2.3.2、磁碟存取原理

上文說過,索引一般以檔案形式儲存在磁碟上,索引檢索需要磁碟I/O操作。與主存不同,磁碟I/O存在機械運動耗費,因此磁碟I/O的時間消耗是巨大的

下圖是磁碟的整體結構示意圖:

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

下圖是磁碟結構的示意圖:

碟片被劃分成一系列同心環,圓心是碟片中心,每個同心環叫做一個磁軌,所有半徑相同的磁軌組成一個柱面。磁軌被沿半徑線劃分成一個個小的段,每個段叫做一個扇區,每個扇區是磁碟的最小儲存單元。為了簡單起見,我們下面假設磁碟只有一個碟片和一個磁頭。

當需要從磁碟讀取資料時,系統會將資料邏輯地址傳給磁碟,磁碟的控制電路按照定址邏輯將邏輯地址翻譯成實體地址,即確定要讀的資料在哪個磁軌,哪個扇區。為了讀取這個扇區的資料,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對準相應磁軌,這個過程叫做尋道,所耗費時間叫做尋道時間,然後磁碟旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間

2.3.3、區域性性原理與磁碟預讀

由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分之一,因此為了提高效率,要儘量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。這樣做的理論依據是電腦科學中著名的區域性性原理

  • 當一個數據被用到時,其附近的資料也通常會馬上被使用。
  • 程式執行期間所需要的資料通常比較集中。
  • 由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高I/O效率。

預讀的長度一般為頁(page)的整倍數頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁(在許多作業系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換資料。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤訊號,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續執行。

2.3.4、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(\log_{d}N)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。

綜上所述,用B-Tree作為索引結構效率是非常高的。

而紅黑樹這種結構,h明顯要深的多。由於邏輯上很近的節點(父子)物理上可能很遠,無法利用區域性性,所以紅黑樹的I/O漸進複雜度也為O(h),效率明顯比B-Tree差很多。

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

floor表示向下取整。由於B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的效能。

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


3、MySQL索引實現

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

3.1、MyISAM索引實現

MyISAM引擎使用B+Tree作為索引結構,葉節點的data域存放的是資料記錄的地址。下圖是MyISAM索引的原理圖:

這裡設表一共有三列,假設我們以Col1為主鍵,則上圖是一個MyISAM表的主索引(Primary key)示意圖。可以看出MyISAM的索引檔案僅僅儲存資料記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:

同樣也是一棵B+Tree,data域儲存資料記錄的地址。因此,MyISAM中索引檢索的演算法會首先按照B+Tree搜尋演算法搜尋索引,如果指定的Key存在,則取出其data域的值,然後以data域的值為地址,讀取相應資料記錄。

MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是為了與InnoDB的聚集索引區分。

3.2、InnoDB索引實現

雖然InnoDB也使用B+Tree作為索引結構,但具體實現方式卻與MyISAM截然不同。

第一個重大區別是InnoDB的資料檔案本身就是索引檔案。從上文知道,MyISAM索引檔案和資料檔案是分離的,索引檔案僅儲存資料記錄的地址。而在InnoDB中,表資料檔案本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域儲存了完整的資料記錄。這個索引的key是資料表的主鍵,因此InnoDB表資料檔案本身就是主索引

上圖是 InnoDB 主索引(同時也是資料檔案)的示意圖,可以看到葉節點包含了完整的資料記錄。這種索引叫做聚集索引因為InnoDB的資料檔案本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識資料記錄的列作為主鍵,如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含欄位作為主鍵,這個欄位長度為6個位元組,型別為長整形。

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

這裡以英文字元的 ASCII 碼作為比較準則。聚集索引這種實現方式使得按主鍵的搜尋十分高效,但是輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同儲存引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白為什麼不建議使用過長的欄位作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的欄位作為主鍵在 InnoDB 中不是個好主意,因為 InnoDB 資料檔案本身是一顆 B+Tree,非單調的主鍵會造成在插入新記錄時資料檔案為了維持 B+Tree 的特性而頻繁的分裂調整,十分低效,而使用自增欄位作為主鍵則是一個很好的選擇。

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

3.3、兩種引擎索引的比較

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域,因此可以擁有更大的出度,擁有更好的效能。只利用索引快速定位資料索引範圍,先定位索引再通過索引高效快速定位資料。

3.4、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以表空間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聚簇索引結構:


4、索引查詢流程

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

通過二級索引查出對應主鍵,拿主鍵回表查主鍵索引得到資料, 二級索引可篩選掉大量無效記錄,提高效率。