1. 程式人生 > >Mysql的儲存引擎和索引

Mysql的儲存引擎和索引

可以說資料庫必須有索引,沒有索引則檢索過程變成了順序查詢,O(n)的時間複雜度幾乎是不能忍受的。我們非常容易想象出一個只有單關鍵字組成的表如何使用B+樹進行索引,只要將關鍵字儲存到樹的節點即可。當資料庫一條記錄裡包含多個欄位時,一棵B+樹就只能儲存主鍵,如果檢索的是非主鍵欄位,則主鍵索引失去作用,又變成順序查找了。這時應該在第二個要檢索的列上建立第二套索引。  這個索引由獨立的B+樹來組織。有兩種常見的方法可以解決多個B+樹訪問同一套表資料的問題,一種叫做聚簇索引(clustered index ),一種叫做非聚簇索引(secondary index)。這兩個名字雖然都叫做索引,但這並不是一種單獨的索引型別,而是一種資料儲存方式。對於聚簇索引儲存來說,行資料和主鍵B+樹儲存在一起,輔助鍵B+樹只儲存輔助鍵和主鍵,主鍵和非主鍵B+樹幾乎是兩種型別的樹。對於非聚簇索引儲存來說,主鍵B+樹在葉子節點儲存指向真正資料行的指標,而非主鍵。

  InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行資料就儲存在葉子節點上,若使用"where id = 14"這樣的條件查詢主鍵,則按照B+樹的檢索演算法即可查詢到對應的葉節點,之後獲得行資料。若對Name列進行條件搜尋,則需要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操作,最終到達葉子節點即可獲取整行資料。

  MyISM使用的是非聚簇索引,非聚簇索引的兩棵B+樹看上去沒什麼不同,節點的結構完全一致只是儲存的內容不同而已,主鍵索引B+樹的節點儲存了主鍵,輔助鍵索引B+樹儲存了輔助鍵。表資料儲存在獨立的地方,這兩顆B+樹的葉子節點都使用一個地址指向真正的表資料,對於表資料來說,這兩個鍵沒有任何差別。由於索引樹是獨立的,通過輔助鍵檢索無需訪問主鍵的索引樹。

  為了更形象說明這兩種索引的區別,我們假想一個表如下圖儲存了4行資料。其中Id作為主索引,Name作為輔助索引。圖示清晰的顯示了聚簇索引和非聚簇索引的差異。

  我們重點關注聚簇索引,看上去聚簇索引的效率明顯要低於非聚簇索引,因為每次使用輔助索引檢索都要經過兩次B+樹查詢,這不是多此一舉嗎?聚簇索引的優勢在哪?

  1 由於行資料和葉子節點儲存在一起,這樣主鍵和行資料是一起被載入記憶體的,找到葉子節點就可以立刻將行資料返回了,如果按照主鍵Id來組織資料,獲得資料更快。

  2 輔助索引使用主鍵作為"指標" 而不是使用地址值作為指標的好處是,減少了當出現行移動或者資料頁分裂時輔助索引的維護工作,使用主鍵值當作指標會讓輔助索引佔用更多的空間,換來的好處是InnoDB在移動行時無須更新輔助索引中的這個"指標"。也就是說行的位置(實現中通過16K的Page來定位,後面會涉及)會隨著資料庫裡資料的修改而發生變化(前面的B+樹節點分裂以及Page的分裂),使用聚簇索引就可以保證不管這個主鍵B+樹的節點如何變化,輔助索引樹都不受影響。

  3 Page結構

  如果說前面的內容偏向於解釋原理,那後面就開始涉及具體實現了。

  理解InnoDB的實現不得不提Page結構,Page是整個InnoDB儲存的最基本構件,也是InnoDB磁碟管理的最小單位,與資料庫相關的所有內容都儲存在這種Page結構裡。Page分為幾種型別,常見的頁型別有資料頁(B-tree Node)Undo頁(Undo Log Page)系統頁(System Page) 事務資料頁(Transaction System Page)等。單個Page的大小是16K(編譯巨集UNIV_PAGE_SIZE控制),每個Page使用一個32位的int值來唯一標識,這也正好對應InnoDB最大64TB的儲存容量(16Kib * 2^32 = 64Tib)。一個Page的基本結構如下圖所示:

  每個Page都有通用的頭和尾,但是中部的內容根據Page的型別不同而發生變化。Page的頭部裡有我們關心的一些資料,下圖把Page的頭部詳細資訊顯示出來:

 

  我們重點關注和資料組織結構相關的欄位:Page的頭部儲存了兩個指標,分別指向前一個Page和後一個Page,頭部還有Page的型別資訊和用來唯一標識Page的編號。根據這兩個指標我們很容易想象出Page連結起來就是一個雙向連結串列的結構。

  再看看Page的主體內容,我們主要關注行資料和索引的儲存,他們都位於Page的User Records部分,User Records佔據Page的大部分空間,User Records由一條一條的Record組成,每條記錄代表索引樹上的一個節點(非葉子節點和葉子節點)。在一個Page內部,單鏈表的頭尾由固定內容的兩條記錄來表示,字串形式的"Infimum"代表開頭,"Supremum"代表結尾。這兩個用來代表開頭結尾的Record儲存在System Records的段裡,這個System Records和User Records是兩個平行的段。InnoDB存在4種不同的Record,它們分別是1主鍵索引樹非葉節點 2主鍵索引樹葉子節點 3輔助鍵索引樹非葉節點 4輔助鍵索引樹葉子節點。這4種節點的Record格式有一些差異,但是它們都儲存著Next指標指向下一個Record。後續我們會詳細介紹這4種節點,現在只需要把Record當成一個儲存了資料同時含有Next指標的單鏈表節點即可。

  User Record在Page內以單鏈表的形式存在,最初資料是按照插入的先後順序排列的,但是隨著新資料的插入和舊資料的刪除,資料物理順序會變得混亂,但他們依然保持著邏輯上的先後順序。

  把User Record的組織形式和若干Page組合起來,就看到了稍微完整的形式。

  現在看下如何定位一個Record:

  1 通過根節點開始遍歷一個索引的B+樹,通過各層非葉子節點最終到達一個Page,這個Page裡存放的都是葉子節點。

  2 在Page內從"Infimum"節點開始遍歷單鏈表(這種遍歷往往會被優化),如果找到該鍵則成功返回。如果記錄到達了"supremum",說明當前Page裡沒有合適的鍵,這時要藉助Page的Next Page指標,跳轉到下一個Page繼續從"Infimum"開始逐個查詢。

  詳細看下不同型別的Record裡到底儲存了什麼資料,根據B+樹節點的不同,User Record可以被分成四種格式,下圖種按照顏色予以區分。

  1 主索引樹非葉節點(綠色)

  1 子節點儲存的主鍵裡最小的值(Min Cluster Key on Child),這是B+樹必須的,作用是在一個Page裡定位到具體的記錄的位置。

  2 最小的值所在的Page的編號(Child Page Number),作用是定位Record。

  2 主索引樹葉子節點(黃色)

  1 主鍵(Cluster Key Fields),B+樹必須的,也是資料行的一部分

  2 除去主鍵以外的所有列(Non-Key Fields),這是資料行的除去主鍵的其他所有列的集合。

  這裡的1和2兩部分加起來就是一個完整的資料行。

  3 輔助索引樹非葉節點非(藍色)

  1 子節點裡儲存的輔助鍵值裡的最小的值(Min Secondary-Key on Child),這是B+樹必須的,作用是在一個Page裡定位到具體的記錄的位置。

  2 主鍵值(Cluster Key Fields),非葉子節點為什麼要儲存主鍵呢?因為輔助索引是可以不唯一的,但是B+樹要求鍵的值必須唯一,所以這裡把輔助鍵的值和主鍵的值合併起來作為在B+樹中的真正鍵值,保證了唯一性。但是這也導致在輔助索引B+樹中非葉節點反而比葉子節點多了4個位元組。(即下圖中藍色節點反而比紅色多了4位元組)

  3 最小的值所在的Page的編號(Child Page Number),作用是定位Record。

  4 輔助索引樹葉子節點(紅色)

  1 輔助索引鍵值(Secondary Key Fields),這是B+樹必須的。

  2 主鍵值(Cluster Key Fields),用來在主索引樹裡再做一次B+樹檢索來找到整條記錄。

  下面是本篇最重要的部分了,結合B+樹的結構和前面介紹的4種Record的內容,我們終於可以畫出一幅全景圖。由於輔助索引的B+樹與主鍵索引有相似的結構,這裡只畫出了主鍵索引樹的結構圖,只包含了"主鍵非葉節點"和"主鍵葉子節點"兩種節點,也就是上圖的的綠色和黃色的部分。

  把上圖還原成下面這個更簡潔的樹形示意圖,這就是B+樹的一部分。注意Page和B+樹節點之間並沒有一一對應的關係,Page只是作為一個Record的儲存容器,它存在的目的是便於對磁碟空間進行批量管理,上圖中的編號為47的Page在樹形結構上就被拆分成了兩個獨立節點。

  至此本篇就算結束了,本篇只是對InnoDB索引相關的資料結構和實現進行了一些梳理總結,並未涉及到Mysql的實戰經驗。這主要是基於幾點原因:

  1 原理是基石,只有充分了解InnoDB索引的工作方式,我們才有能力高效的使用好它。

  2 原理性知識特別適合使用圖示,我個人非常喜歡這種表達方式。

  3 關於InnoDB優化,在《高效能Mysql》裡有更加全面的介紹,對優化Mysql感興趣的同學完全可以自己獲取相關知識,我自己的積累還未達到能分享這些內容的地步。

  另:對InnoDB實現有更多興趣的同學可以看看Jeremy Cole的部落格(參考文獻三篇文章的來源),這位老兄曾先後在Mysql,Yahoo,Twitter,Google從事資料庫相關工作,他的文章非常棒!