MySQL:索引工作原理

逸卿發表於2014-08-04

為什麼需要索引(Why is it needed)?

當資料儲存在磁碟類儲存介質上時,它是作為資料塊存放。這些資料塊是被當作一個整體來訪問的,這樣可以保證操作的原子性。硬碟資料塊儲存結構類似於連結串列,都包含資料部分,以及一個指向下一個節點(或資料塊)的指標,不需要連續儲存。

記錄集只能在某個關鍵欄位上進行排序,所以如果需要在一個無序欄位上進行搜尋,就要執行一個線性搜尋(Linear Search)的過程,平均需要訪問N/2的資料塊,N是表所佔據的資料塊數目。如果這個欄位是一個非主鍵欄位(也就是說,不包含唯一的訪問入口),那麼需要在N個資料塊上搜尋整個表格空間。

但是對於一個有序欄位,可以運用二分查詢(Binary Search),這樣只要訪問log2 (N)的資料塊。這就是為什麼效能能得到本質上的提高。


什麼是索引(What is indexing)?

索引是對記錄集的多個欄位進行排序的方法。在一張表中為一個欄位建立一個索引,將建立另外一個資料結構,包含欄位數值以及指向相關記錄的指標,然後對這個索引結構進行排序,允許在該資料上進行二分法排序。

副作用是索引需要額外的磁碟空間,對於MyISAM引擎而言,這些索引是被統一儲存在一張表中的,這個檔案將很快到達底層檔案系統所能夠支援的大小限制,如果很多欄位都建立了索引的話。


索引如何工作(How does it work?)

首先,我們建立一個示範資料庫表:

欄位名       資料型別      大小
id (Primary key) Unsigned INT   4 bytes
firstName        Char(50)       50 bytes
lastName         Char(50)       50 bytes
emailAddress     Char(100)      100 bytes
注意:使用char是為了指定準確的磁碟佔用大小。這個示範資料庫包含500萬行,而且沒有索引。我們將分析一些查詢語句的效能,一個是使用主鍵id(有序)查詢,一個是使用firstName(非關鍵無序欄位)。

例1

我們的示範資料庫有r=5,000,000條記錄,每條記錄長度R=204位元組而且使用MyISAM引擎儲存(預設資料塊大小為B=1024位元組),這張表的塊因子(blocking factor)會是bfr = (B/R) = 1024/204 = 5 條記錄每磁碟資料塊。儲存這張表所需要的磁碟塊為N = (r/bfr) = 5000000/5 = 1,000,000 blocks。

在id欄位上的線性搜尋平均需要N/2 = 500,000塊訪問來找到一條記錄假設id欄位是查詢關鍵值,不過既然id欄位是有序的,可以執行一個二分查詢,這樣平均只需要訪問log2 (1000000) = 19.93 = 20 個資料塊。我們馬上就看到了極大的提高。

現在firstName欄位既不是有序的,無法執行二分搜尋,數值也不具有唯一性,所以對這張表的查詢必須到最後一個記錄即全表掃描N = 1,000,000個資料塊訪問。這就是索引用來改進的地方。

假如索引記錄只包含一個索引列以及一個指向原記錄資料的指標,那麼它顯而易見會比原記錄(多列)要小。所以索引本身所需要的磁碟塊要更少,掃描數目也少。firstName索引表結構如下:

Field name       Data type      Size on disk
firstName        Char(50)       50 bytes
(record pointer) Special        4 bytes
注意: MySQL裡的指標按表大小的不同分別可能是 2, 3, 4 或 5 個位元組。

例2

假設我們的資料庫有r = 5,000,000 條記錄,建立了一個長R = 54位元組的索引,並且使用預設磁碟塊大小為1,024位元組。那麼該索引的塊因子為bfr = (B/R) = 1024/54 = 18 條記錄每磁碟塊。容納這個索引表總共需要的磁碟塊為N = (r/bfr) = 5000000/18 = 277,778 塊。

現在使用FirstName欄位來進行搜尋就可以利用索引來提高效能。這允許使用一個二分查詢,平均log2 (277778) = 18.08 -> 19次資料塊訪問。找到實際記錄的地址,這需要進一步的塊讀取,這樣總數達到19 + 1 = 20次資料塊訪問,這和非索引表的資料塊訪問次數有天壤之別。


什麼時候使用索引(When should it be used?)

鑑於建立索引需要額外的磁碟空間(上面的例子需要額外的277778個磁碟塊),以及太多的索引會導致檔案系統大小限制所產生的問題,所以對哪些欄位建立索引,什麼情況下使用索引,需要審慎考慮。

由於索引只是用來加速資料查詢,那麼顯然對只是用來輸出的欄位建立索引會浪費磁碟空間以及發生插入、刪除操作時的處理時間,所以這種情況下應該儘量避免。此外鑑於二分搜尋的特性,資料的基數或獨立性是很重要的。在基數為2的欄位上建立索引,將把資料分割一半,而基數為1000則將返回大約1000條記錄。低基數的二分查詢效率將降低為一個線性排序,而且查詢優化器可能會在基數小於記錄數某個比例時(如30%)的情況下將避免使用索引而直接查詢原表,所以這種情況下的索引浪費了空間。

相關文章