數據庫管理系統索引技術概述
2018年4月23日 Wray Zheng9
文章目錄
為什麼需要索引?
索引中的一些概念
索引如何提高查詢效率?
索引的分類
稀疏索引與稠密索引
主索引與輔助索引
聚簇索引和非聚簇索引
總結
為什麼需要索引?
我們知道,磁盤的讀寫效率是比較低的,以傳統機械硬盤為例,讀寫時涉及到讀寫頭的尋道和定位,這部分時間開銷可能比實際讀寫數據時所花的時間還要長。即使是固態硬盤,由於數據的存儲可能是散落在各個磁盤塊中,通過指針連接起來,因此訪問數據時需要對磁盤進行多次讀寫,同樣會帶來效率上的問題。
再來看數據庫的存儲,數據庫中的一個表可能存儲在多個文件中,而每個文件包含了多個磁盤塊(扇區),我們討論最好的情況,也就是所有記錄都是按查詢的字段進行排序,那麼此時可以利用二分法等高效的算法進行搜索。如果是在內存中進行這種搜索,log(n) 的時間確實非常高效,但放在磁盤中就未必了,為什麼這麼說?
因為這些高效算法通常都是在內存中操作的,也就是說數據都已經被加載到內存中。而一個表中包含的數據量可能很大,沒辦法將這些數據一次性裝載到內存中,因此我們需要通過多次讀寫磁盤來完成這些操作。這樣一來,磁盤本身的文件組織方式就會對算法的效率造成影響。
比如我們用二分法來查找數據,二分法是建立在數據能夠被隨機訪問的基礎上的,這樣可以計算出中間位置,並直接訪問該位置。如果磁盤塊是連續的還好,假設每條記錄定長,那麼我們可以得到中間位置的扇區號,直接訪問該扇區,從而得到目標記錄;但如果磁盤塊不連續,那麼只能通過指針進行連接,這樣我們就沒辦法直接得出中間位置的扇區號,只能從第一個磁盤塊開始,依次訪問它的下一個磁盤塊,也就是順序訪問,即使記錄是有序的,二分法也沒有用武之地,只能從頭遍歷記錄進行查找。
總結上述提到的問題,就是當數據量大的時候,數據無法被一次性加載到內存中,因此對數據的查找操作受限於文件在磁盤中的存儲方式,特別是利用指針來連接不連續磁盤塊的情況,極大影響查詢效率。
因此,我們想到了用索引來定位記錄,提高查詢的效率。
索引中的一些概念
以字典為例,每個字及其解釋都可以看作一條記錄,這條記錄大致可分為幾個字段:字、讀音、解釋。
我們是如何查找一個字對應的記錄呢?通過目錄。
因此目錄就相當於 索引,目錄中記錄了每個字及其對應的頁碼,相當於一個個 索引項。
我們是根據什麼來找到索引項的?對了,是字。因此字就是用於索引的字段,叫做 索引字段,頁碼相當於一條記錄的實際存儲地址,等價於指向記錄所在磁盤位置的指針。
存儲索引項的文件稱為 索引文件,而存儲實際數據(表)的文件稱為 主文件。
索引如何提高查詢效率?
索引的意義,就在於它只抽取了原記錄中的一部分關鍵的信息,並與記錄的位置建立關聯,以此定位記錄在磁盤上的真正位置。
索引存儲的數據較少,更容易被加載到內存中,也就意味著我們可以通過高效的算法在索引上查找目標記錄的索引項,得到目標記錄在磁盤上的位置,然後直接讀取該記錄。
例如以字段 A 作為索引,那麼每個索引項只存 A 的值,以及 A 對應記錄所在磁盤的位置。這樣一來,我們通常可以將索引文件加載到內存中,然後根據待查詢記錄字段 A 的值,找到索引項,得到該記錄在磁盤中的位置,就能直接到特定磁盤塊將目標記錄讀取出來。
索引的分類
稀疏索引與稠密索引
首先說說稀疏索引。
稀疏索引只包含了索引字段中一部分的值,通過這些值可以確定目標記錄的範圍,然後再到這個範圍中順序查找。因此,稀疏索引要求主文件必須按照索引字段進行排序,通常索引文件本身也有相同的排序關係。
下面會講到主索引,它是一種特殊的稀疏索引,它的索引項並不是指向記錄,而是指向記錄所在的存儲塊。也就是說,一個存儲塊對應一個索引項。
再來說說稠密索引。
稠密索引,顧名思義,就是索引項非常稠密,到什麼程度呢?每個索引字段的值都對應一個索引項。
如果索引字段沒有重複值,那麼索引和記錄就是一一對應的關係:
如果索引字段包含重複的值,有三種索引策略。
一是索引中包含重複值:
此時索引項和記錄也是一一對應的關係。
二是索引中不包含重複值,主文件按索引字段排序:
因為索引字段值相同的記錄是連續放在一起的,因此索引項只需指向索引字段值相同記錄中的第一條記錄。
三是索引中不包含重複值,主文件不按索引字段排序:
這裡引入了一箇中間層。因為主文件中索引字段值存在重複,並且沒有按照索引字段排序,因此必須對每條記錄建立一個索引,才能由索引文件找到主文件中的記錄。但是由於索引中不包含重複值,因此我們可以引入一箇中間層,讓索引項不直接指向記錄,而是指向中間層。中間層的指針桶與記錄一一對應,並且索引字段值相同的記錄對應的指針桶是連續存放的,這樣就等價於中間層是按索引字段進行排序。
我們來總結一下稠密索引:
若索引字段不重複,則索引與記錄自然一一對應;
若索引字段重複,要麼讓索引重複,這樣索引和記錄也可以一一對應;
要麼索引不重複,這就要求索引指向的結構是按索引字段排序的(中間層也可以認為是按索引字段排序),這樣才可以僅僅指向索引字段值相同記錄中的第一條記錄。
主索引與輔助索引
主索引通常是針對每個存儲塊建立一個索引項,索引項的個數與存儲表所佔的磁盤塊數相同。
存儲表中位於每一存儲塊的第一條記錄稱為錨記錄,或稱為塊錨。
主索引和主文件通常都是按照索引字段進行排序,如前面所說,主索引是稀疏索引。
輔助索引是稠密索引,它是建立在一個或多個非排序字段上的輔助存儲結構,通常不同的索引字段值對應一個索引,如果有重複的索引字段值,則用類似鏈表的結構來存儲具有相同索引字段值記錄的位置,也就是前面提到的引入中間層的策略。
總結和對比主索引與輔助索引:
主索引是稀疏索引,輔助索引是稠密索引;
一個主文件只能有一個主索引,但可以有多個輔助索引;
主索引通常建立在主碼/排序碼上,輔助索引建立在其它屬性上;
可以利用主索引重新組織主文件數據,但不能利用輔助索引來改變主文件數據。
聚簇索引和非聚簇索引
聚簇索引 —— 索引中鄰近的記錄在主文件中也是鄰近存儲的;
非聚簇索引 —— 索引中鄰近的記錄在主文件中不一定是臨近存儲的。
如果主文件的某一排序字段取值不唯一,那麼該字段就稱為聚簇字段。聚簇索引通常定義在聚簇字段上;
聚簇索引通常是對聚簇字段上的每一個不同值建立一個索引項;
一個主文件只能有一個聚簇索引文件,但可以有多個非聚簇索引文件;
主索引通常是聚簇索引,輔助索引通常是非聚簇索引;
主索引/聚簇索引能夠決定記錄的存儲位置,而非聚簇索引只能用於查詢已存儲記錄的位置。
總結
這篇文章對索引進行了概括性的介紹,說明了索引在數據庫查詢中的意義,並介紹了索引的幾種分類方式。
有關索引的具體實現,包括 B+ 樹索引以及散列索引,後續將會單獨進行介紹。
閱讀更多 架構師交流圈 的文章