[貝聊科技]理解數據庫索引

做者:陳浩 前端開發部 iOS 開發工程師html

在平常開發中,數據庫下降了咱們操做數據的門檻,咱們只要編寫特定的 SQL,就能對數據作增刪改查操做。在簡化的背後,每每都隱藏着性能優化的福利,數據庫也是如此,咱們知道假如沒有索引,查詢數據就會全表掃描,而索引就如書的目錄通常,大大提升了查詢效率。本文將對數據庫索引進行介紹,認識索引的數據結構,同時也介紹索引的其餘概念。前端

索引的數據結構

索引在本質上是爲了優化查找的速度,對於給定的數據,咱們可使用順序查找,若是數據已經排好序,咱們可使用二分查找,若是查找的數據量不大,咱們能夠構造二叉查找樹將查找放在內存中,而索引的數據結構是由平衡二叉樹演化而來,在正式介紹索引的數據結構以前,讓咱們先來看看二叉查找樹。mysql

二叉查找樹

二叉查找樹要求左子樹的鍵值老是小於根的鍵值,右子樹的鍵值老是大於根的鍵值。算法

1527218750414

1527217930423

二叉查找樹的問題是假如單支過長就會大大影響其查找效率,甚至退化成順序查找sql

1527218408243

爲了提升二叉樹的查找效率,須要構造的這棵二叉樹是平衡的——平衡二叉樹要求任何結點的兩個子樹的高度最大差爲1。平衡二叉樹一般須要左旋、右旋來達到平衡。數據庫

1527219158761

B* 樹

B- 樹

咱們先來看看對 B 樹的描述:性能優化

  • B 樹的 B 不是表示二叉,而是表示平衡
  • B 樹並非一顆二叉樹,B 樹是 n 叉的(n > 2)
  • 每一個結點有多個關鍵字,關鍵字之間又有指向孩子結點的指針
  • 一個結點內的關鍵字都有序排列
  • 全部葉子結點都在同一層

1527237093708

對於查找而言,B 樹的查找相似二叉樹,由於每一個結點內的關鍵字都是排序好的 key[1...n],咱們可運用二分查找將查找關鍵字 k 與 key[i] 比較,從而找出相應區間的子樹。數據結構

B 樹查找的簡化代碼:性能

Result BTreeSearch(BTNode *t, KeyType k) {
    BTNode *p = t; *q = NULL; // q 指向 p 的雙親
    int found = 0, i = 0;
    while (p != NULL && found == 0) {
        i = BinarySearch(p, k);
        if (i > 0 && p->key[i] == k) {
            found = 1;
        } else {
            q = p;
            p = p->ptr[i];
        }
    }
    ...
}
複製代碼

上述代碼也可使用遞歸。有了這些基本的認識後,不難發現 B 樹的查找效率與樹的高度有關,高度越小,查找的次數就越少。學習

接下來看看 B 樹的插入和刪除。 對插入而言,若是該結點還有空位置,直接插入,不然,會將結點分紅兩部分,中間位置的關鍵字插入到父結點中,若是父結點也不知足,再往上插,直到這個過程傳到根結點。

1527237417928

插入15

刪除比插入稍微複雜一點,若是刪除一個關鍵字後,結點的關鍵字個數沒有少於它的裝填因子,則直接刪除

1527237872376

刪除8,16

不然分兩種狀況:

  • 若是左(右)兄弟結點的關鍵字個數大於裝填因子,則將左(右)結點最大或最小關鍵字上移到父結點,再把父結點中大於或小於上移關鍵字的關鍵字下移到要被刪除的結點中。

1527582084766

刪除15

  • 若是左(右)兄弟結點的關鍵字個數等於裝填因子,這時須要把刪除關鍵字的結點與左(右)結點關鍵字和分割兩者關鍵字的雙親結點關鍵字合併成一個結點,若是所以使雙親結點關鍵字個數小於裝填因子,則對雙親結點也作一樣處理,以至可能使整棵樹的高度減小一層。

1527238316921

刪除4後的結果

由上可知,B 樹的插入和刪除都是須要代價的,因此咱們對數據庫索引的創建也須要特別謹慎,不然不合理的索引反而下降了效率。

B+ 樹

B+ 樹是 B- 樹的變形,經常使用於索引結構中,它與 B- 樹的主要差別有:

  • B+ 樹中全部葉子結點包含了所有關鍵字,即非葉子結點的關鍵字也出如今葉子結點中
  • 葉子結點的指針再也不指向另外一級索引,而是直接指向數據文件的記錄
  • 分支結點不包含關鍵字對應的存儲地址,只包含指向各個子結點的指針
  • 全部葉子結點連接成一個線性鏈表

1527582980853

B 樹和平衡二叉樹的一個重要區別是結點的大小及其形成的樹的高度不一樣,B+ 樹的結點大小通常是一個磁盤塊的大小,也就是數據頁的大小,所以 B 樹矮而胖,二叉樹高而瘦。前面已經提到 B 樹的查找效率和其高度有關,假設當前數據表的數據爲 N,每一個磁盤塊的數據項的數量是 m,則有 h = ㏒(m+1)N,而 m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小又是固定的,故數據項的大小越小,樹的高度也就越小。這就是爲何要求索引字段儘量小的緣由。同理,將數據不存儲在分支結點,也是爲了儘量多的存放數據項。

B+ 樹索引

B+ 樹索引就是 B+ 樹在數據庫中的實現。B+ 索引在數據庫中有一個特色是高扇出性,所以在數據庫中,B+ 樹的高度通常都在2~4層,這也就是說查找某一鍵值的行記錄時最多隻須要2到4次 IO。

B+ 樹索引並不能找到一個給定鍵值的具體行。B+樹索引能找到的只是被查找數據行所在的頁。而後數據庫經過把頁讀入到內存,再在內存中進行查找,最後獲得要查找的數據。

彙集索引和輔助索引

B+ 樹索引可分爲彙集索引和輔助索引(secondary index),它們的主要區別是彙集索引要求以惟一的 key(通常是主鍵)來構造索引,文件中記錄的物理存儲順序和索引順序一致,因爲實際的數據頁只能按照一棵 B+ 樹進行排序,所以每張表只能擁有一個彙集索引,輔助索引的 key 能夠不是惟一的,輔助索引能提升彙集索引之外 key 的查找性能,這也會增長必定的開銷。

下面表有三個列,分別是 id(主鍵)、name 和 salary,咱們來看看彙集索引和輔助索引的原理:

1527560106240

以上是彙集索引

1527560883816

以上是輔助索引

聯合索引

咱們已經介紹過了在單個列上使用索引,聯合索引是指對錶上的多個列進行索引,聯合索引的本質也是一棵 B+ 樹,下面看看聯合索引的內部結構:

dff64b61-998e-47be-910d-02b596227f71

對上圖而言,(1,1)、(1,2)、(2,1)、(2,4)、(3,1)、(3,2),數據按(a,b)的順序進行了存放,第一列是升序排序的,第二列是根據第一列排序而排序的。

所以,對於查詢 SELECT*FROM TABLE WHERE a=xxx and b=xxx,顯然是可使用(a,b)這個聯合索引的。對於單個的a列查詢 SELECT*FROM TABLE WHERE a=xxx,也可使用這個(a,b)索引。但對於b列的查詢 SELECT*FROM TABLE WHERE b=xxx,則不可使用這棵B+樹索引。能夠發現葉子節點上的b值爲一、二、一、四、一、2,顯然不是排序的,所以對於b列的查詢使用不到(a,b)的索引。

聯合索引能在索引到第一個鍵值後對第二個鍵值進行排序。例如,查詢某個用戶的購物狀況,並按照時間進行排序,最後取出最近三次的購買記錄,這時使用聯合索引能夠避免多一次的排序操做,由於索引到某個用戶 id 後,購買記錄已是有序的了。

正如前面所介紹的那樣,聯合索引(a,b)實際上是根據列a、b進行排序,所以語句 SELECT...FROM TABLE WHERE a=xxx ORDER BY b 能夠直接使用聯合索引獲得結果。

然而對於聯合索引(a,b,c)來講,語句 SELECT...FROM TABLE WHERE a=xxx AND b=xxx ORDER BY cSELECT...FROM TABLE WHERE a=xxx ORDER BY b 一樣能夠直接經過聯合索引獲得結果。

但對於語句 SELECT...FROM TABLE WHERE a=xxx ORDER BY c,聯合索引不能直接獲得結果,由於 c 是用不到索引的。

這就是索引最左前綴匹配的特性。根據該原則,咱們創建聯合索引時要考慮好查詢儘量地用得上索引,這也要求咱們儘量選擇區分度高的列做爲索引。

小結

  • 在做爲主鍵的列上,強制該列的惟一性和組織表中數據的排列結構。
  • 在常常用在鏈接的列上,這些列主要是一些外鍵,能夠加快鏈接的速度。
  • 在常常須要根據範圍進行搜索的列上建立索引,由於索引已經排序,其指定的範圍是連續的。
  • 在常常須要排序的列上建立索引,由於索引已經排序,這樣查詢能夠利用索引的排序,加快排序查詢時間。
  • 在常用在where子句中的列上面建立索引,加快條件的判斷速度。

哈希索引和位圖索引

最後再簡單介紹下另外兩種索引結構

哈希索引

哈希索引經過哈希算法來實現查找,其衝突解決採用鏈地址法,咱們知道哈希算法的時間複雜度爲 O(1),因此哈希索引是很是高效的。

由於哈希索引的記錄不以任何特定方式排序,這也致使哈希索引沒法應用在範圍查找中。

位圖索引

位圖索引(bitmap index)是爲多個列查詢設計的特殊索引,位圖索引適合用於列上的值大量重複出現。

表結構:

ID gender income_level
43123 m L1
65654 f L2
76534 f L1
12343 m L4
65765 f L3

gender 的位圖:

m 10010
f 01101

income_level 的位圖:

L1 10100
L2 01000
L3 00001
L4 00010
L5 00000

上述表對於只以性別爲條件的查詢,位圖索引並不能帶來什麼性能的提高。然而對查詢 Select * from t where gender = 'f' and income_level = 'L3',位圖索引會執行兩個位圖的交操做(邏輯與)。即 gender 的位圖 = f(01101) 和 income_level 的位圖 = L2(01000) 的交獲得位圖 01000。顯然對於多個列上大量重複數據項的查詢,位圖索引能夠提升查找效率。此外,位圖索引還有體積小的優勢。

總結

本文是我學習數據庫索引的筆記,僅僅介紹了數據庫的幾種索引的原理,並無深刻到更加底層的研究,只能對平常開發中如何創建索引、選擇索引發到必定的指示做用,而對於查詢性能的優化仍是須要從大量的實踐中總結出經驗。

參考文章

MySQL索引原理及慢查詢優化

MySQL索引背後的數據結構及算法原理

相關文章
相關標籤/搜索