索引實現靠的是樹,你知道麼?

之前朋友在面試的時候被問到了許多關於索引的問題,而索引這個詞一直也是我們在開發中最最最常見的,也是很多在進行代碼優化的時候會去做的一件事情,所以今天我們來說說面試中關於索引的那點事。

索引

什麼是索引?

索引其實是數據庫的一種術語,在關係數據庫中,索引是一種單獨的、物理的對數據庫表中一列或多列的值進行排序的一種存儲結構,它是某個表中一列或若干列值的集合和相應的指向表中物理標識這些值的數據頁的邏輯指針清單。索引的作用相當於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。

索引提供指向存儲在表的指定列中的數據值的指針,然後根據您指定的排序順序對這些指針排序。數據庫使用索引以找到特定值,然後順指針找到包含該值的行。這樣可以使對應於表的SQL語句執行得更快,可快速訪問數據庫表中的特定信息。

但是面試的時候一般不會問你索引是什麼?而是喜歡去問,爲什麼要去使用索引,它的底層是怎麼實現的?那數據庫又應該怎麼去優化呢?下面我們就從這三個方面去解釋一下這些面試中的要點信息。

索引底層實現

索引的實現通常使用B樹及其變種B+樹。

B-Tree 是最常用的用於索引的數據結構。因爲它們是時間複雜度低, 查找、刪除、插入操作都可以可以在對數時間內完成。另外一個重要原因存儲在B-Tree中的數據是有序的。

哈希表是另外一種你可能看到用作索引的數據結構-這些索引通常被稱爲哈希索引。使用哈希索引的原因是,在尋找值時哈希表效率極高。所以,如果使用哈希索引,對於比較字符串是否相等的查詢能夠極快的檢索出的值。

我們以MySQL數據庫的索引爲例子。

既然說到了索引的實現是通過B樹和變種B+樹,那我們來說說這個B樹和B+樹。

B樹

我們看個圖。圖中所示,B樹事實上是一種平衡的多叉查找樹,也就是說最多可以開m個叉(m>=2),我們稱之爲m階b樹,我給大家多畫一點內容,這也是我專門看視頻的時候記錄下的一些自己的心得。
在這裏插入圖片描述

圖中其實畫的是比較簡單的,但是如果說我們畫一個三階的,這個時候就可以是這樣子的
在這裏插入圖片描述
這就是說最多能開三個叉,但是裏面有可能有兩個叉的,以最多的叉來算。

m階B樹滿足以下條件

  • 每個節點至多可以擁有m棵子樹。
  • 根節點,只有至少有2個節點(要麼極端情況,就是一棵樹就一個根節點,單細胞生物,即是根,也是葉,也是樹)。
  • 非根非葉的節點至少有的Ceil(m/2)個子樹(Ceil表示向上取整,圖中3階B樹,每個節點至少有2個子樹,也就是至少有2個叉)。
  • 非葉節點中的信息包括[n,A0,K1,A1,K2,A2,…,Kn,An],,其中n表示該節點中保存的關鍵字個數,K爲關鍵字且Ki<Ki+1,A爲指向子樹根節點的指針。
  • 從根到葉子的每一條路徑都有相同的長度,也就是說,葉子節在相同的層,並且這些節點不帶信息,實際上這些節點就表示找不到指定的值,也就是指向這些節點的指針爲空。

B樹的查詢過程和二叉排序樹比較類似,從根節點依次比較每個結點,因爲每個節點中的關鍵字和左右子樹都是有序的,所以只要比較節點中的關鍵字,或者沿着指針就能很快地找到指定的關鍵字,如果查找失敗,則會返回葉子節點,即空指針。

B樹搜索的簡單僞算法如下:

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

對於每個結點,主要包含一個關鍵字數組Key[],一個指針數組(指向兒子)Son[]。在B-Tree內,查找的流程是:使用順序查找(數組長度較短時)或折半查找方法查找Key[]數組,若找到關鍵字K,則返回該結點的地址及K在Key[]中的位置;否則,可確定K在某個Key[i]和Key[i+1]之間,則從Son[i]所指的子結點繼續查找,直到在某結點中查找成功;或直至找到葉結點且葉結點中的查找仍不成功時,查找過程失敗。

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

B+樹

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

B+ 樹是一種樹數據結構,是一個n叉樹,每個節點通常有多個孩子,一顆B+樹包含根節點、內部節點和葉子節點。根節點可能是一個葉子節點,也可能是一個包含兩個或兩個以上孩子節點的節點。

B+ 樹通常用於數據庫和操作系統的文件系統中。

NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系統都在使用B+樹作爲元數據索引。

B+ 樹的特點是能夠保持數據穩定有序,其插入與修改擁有較穩定的對數時間複雜度。

B+ 樹元素自底向上插入。
在這裏插入圖片描述

看節點之間有重複元素,而且在葉子節點上還用指針連接在了一起,而這些就是B+樹的幾個特點

  1. 每個父節點的元素都出現在了子節點中,分別是子節點最大或者最小的元素。

  2. 在上面的這一棵樹中,根節點元素8是子節點258的最大的元素,根元素15也是。這時候要注意了,根節點最大的元素等同於整個B+樹的最大的元素,以後無論是怎麼插入或者是刪除,始終都要保持最大的元素在根節點中。

  3. 葉子節點,因爲父節點的元素都出現在了子節點當中,因此所有的葉子節點包含了全量的元素信息。

那麼既然B+樹是B樹的的一種變形樹,那麼差異點在哪呢?

B+樹與B樹差異

有k個子節點的節點必然有k個元素

非葉子節點僅具有索引作用,跟記錄有關的信息均存放在葉子節點中

樹的所有葉子節點構成一個有序鏈表,可以按照元素排序的次序遍歷全部記錄

B樹和B+樹的區別在於,B+樹的非葉子節點只包含導航信息,不包含實際的值,所有的葉子節點和相連的節點使用鏈表相連,便於區間查找和遍歷。

B+樹的優點

由於B+樹在內部節點上不包含數據信息,因此在內存頁中能夠存放更多的key。 數據存放的更加緊密,具有更好的空間局部性。因此訪問葉子節點上關聯的數據也具有更好的緩存命中率。

B+樹的葉子節點都是相連的,因此只要遍歷葉子節點就可以實現整顆樹的遍歷。而且由於數據順序排列並且相連,所以便於區間查找和搜索。而B樹則需要進行每一層的遞歸遍歷,相鄰的元素可能在內存中不相鄰,所以緩存命中性沒有B+樹好。

B+樹的缺點

但是B樹也有優點,其優點在於,由於B樹的每一個節點都包含key和value,因此經常訪問的元素可能離根節點更近,因此訪問也更迅速。

因爲B+樹比B樹的讀寫代價更低,所以B+樹比B樹更適合操作系統的文件索引和數據庫索引。

那麼既然基層實現我們理解完了,是不是該說一下這個數據庫索引分類了呢?

數據庫索引分類

根據數據庫的功能,可以在數據庫設計器中創建四種索引:普通索引、唯一索引、主鍵索引和聚集索引。

普通索引

最基本的索引類型,沒有唯一性之類的限制。普通索引可以通過以下幾種方式創建:創建索引,例如 CREATE INDEX <索引的名字> ON tablename (列的列表)

修改表,例如 ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表)

創建表的時候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) )

唯一索引

唯一索引是不允許其中任何兩行具有相同索引值的索引。

當現有數據中存在重複的鍵值時,大多數數據庫不允許將新創建的唯一索引與表一起保存。數據庫還可能防止添加將在表中創建重複鍵值的新數據。例如,如果在 employee 表中職員的姓 (lname) 上創建了唯一索引,則任何兩個員工都不能同姓。

對某個列建立UNIQUE索引後,插入新記錄時,數據庫管理系統會自動檢查新紀錄在該列上是否取了重複值,在CREATE TABLE 命令中的UNIQE約束將隱式創建UNIQUE索引。
創建唯一索引的幾種方式:

創建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表)

修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表) ;

創建表的時候指定索引,例如 CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) )

主鍵索引

簡稱爲主索引,數據庫表中一列或列組合(字段)的值唯一標識表中的每一行。該列稱爲表的主鍵。在數據庫關係圖中爲表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。提示儘管唯一索引有助於定位信息,但爲獲得最佳性能結果,建議改用主鍵索引。

聚集索引

也稱爲聚簇索引,在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引, 即如果存在聚集索引,就不能再指定CLUSTERED 關鍵字。

索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。聚集索引更適用於對很少對基表進行增刪改操作的情況。

如果在表中創建了主鍵約束,SQL Server將自動爲其產生唯一性約束。在創建主鍵約束時,指定了CLUSTERED關鍵字或乾脆沒有制定該關鍵字,SQL Sever將會自動爲表生成唯一聚集索引。

而如果是面試過程中很多就會問你是索引的分類和怎麼使用的,最後我們再來說怎麼操作索引。

操作索引

創建索引

SQL3沒有提供建立索引的方法。但是,從事DBMS開發、銷售的公司都提供他們具有這種功能的SQL工具。因爲這些工具不是標準化的,它們相互不同。SQL語言使用CREATE INDEX 語句建立索引,其一般格式是:


CREATE [UNIQUE] [CLUSTERED| NONCLUSTERED] INDEX <索引名> ON <表名>(<列名>[ASC|DESC] [, <列名>[ASC|DESC]...])

說明:與表一樣,索引也需要有唯一的名字,且基於一個表來建立,可以根據表中的一列或者多列,當列的順序都是升序默認可不必標出,當屬性列有按照降序排列的,所有屬性的升序降序都不要標明。

– UNIQUE——建立唯一索引。

– CLUSTERED——建立聚集索引。

– NONCLUSTERED——建立非聚集索引。

– ASC——索引升序排序。

– DESC——索引降序排序。

修改索引
對於已經建立的索引,如果需要對其重新命名,可以使用ALTER INDEX 語句。其一般格式爲

ALTER INDEX <舊引索名字> RENAME TO<新引索名>

刪除索引

當某個時期基本表中數據更新頻繁或者某個索引不再需要時,需要刪除部分索引。SQL語言使用DROP INDEX 語句刪除索引,其一般格式是:

DROP INDEX<索引名>

刪除索引時,DBMS不僅在物理刪除相關的索引數據,也會從數據字典刪除有關該索引的描述。

關於索引你會了嗎?

我是懿,一個正在被打擊還在努力前進的碼農。歡迎大家關注我們的公衆號,加入我們的知識星球,我們在知識星球中等着你的加入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章