MySQL——索引實現原理

CoderFocus發表於2018-10-30

在MySQL中,索引屬於儲存引擎級別的概念,不同儲存引擎對索引的實現方式是不同的,本文主要討論MyISAM和InnoDB兩個儲存引擎的索引實現方式。

MyISAM索引實現

MyISAM引擎使用B+Tree作為索引結構。

MyISAM會按照資料插入的順序分配行號,從0開始,然後按照資料插入的順序儲存在磁碟上。因為行是定長的,所以可以從表的開頭跳過相應的位元組找到需要的行。

mark

MyISAM的一級索引(主鍵索引),一個節點包含多個內部節點,索引中的每個葉子節點包含“行號”。假設我們以col1為主鍵,則下圖是一個MyISAM表的主索引(Primary key)示意。

mark

可以看出MyISAM的索引檔案僅僅儲存資料記錄的行號,然後通過此行號回表查詢需要的資料。

那col2列上的索引(輔助索引)又會怎麼樣呢?有什麼特別之處嗎?答案是否定的,和一級索引(主鍵索引)沒有什麼區別。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在col2上建立一個輔助索引,則此索引的結構如下圖所示:

mark

因此,MyISAM中索引檢索的演算法為首先按照B+Tree搜尋演算法搜尋索引,如果指定的Key存在,則取出其data域的值,然後以data域的值為地址,讀取相應資料記錄。MyISAM的索引方式索引和資料存放是分開的,非聚集”的,所以也叫做非聚集索引。

InnoDB索引實現

雖然InnoDB也使用B+Tree作為索引結構,但具體實現方式卻與MyISAM截然不同。因為InnoDB支援聚簇索引(主鍵索引),聚簇索引就是表,所以InnoDB不用像MyISAM那樣需要獨立的行儲存。也就是說,InnoDB的資料檔案本身就是索引檔案。

聚簇索引的每一個葉子節點都包含了主鍵值、事務ID、用於事務和MVCC的回滾指標以及所有的剩餘列。假設我們以col1為主鍵,則下圖是一個InnoDB表的聚簇索引(主鍵索引)(Primary key)示意。

mark

與MyISAM不同的是,InnoDB的二級索引和聚簇索引很不相同。InnoDB的二級索引的葉子節點儲存的不是行號(行指標),而是主鍵列。這種策略的缺點是二級索引需要兩次索引查詢,第一次在二級索引中查詢主鍵,第二次在聚簇索引中通過主鍵查詢需要的資料行。

畫外音:可以通過我們前面提到過的索引覆蓋來避免回表查詢,這樣就只需要一次回表查詢,對於InnoDB而言,就是隻需要一次索引查詢就可以查詢到需要的資料記錄,因為需要的資料記錄已經被索引到二級索引中,直接就可以找到。

好處是InnoDB在移動行時無需更新一級索引中的這個”指標“,因為主鍵是不會改變的,但是行指標卻會改變。

InnoDB的二級索引示意如圖:

mark

使用InnoDB主鍵應該知道的事項

因為InnoDB的索引的方式通過主鍵聚集資料,嚴重依賴主鍵。索引如果沒有定義主鍵,那麼InnoDB會選擇一個唯一的非空索引代替。如果沒有這樣的索引,InnoDB會隱式定義一個主鍵來作為聚簇索引。

聚簇索引的優點有:

1.可以把相關資料儲存在一起,減少資料查詢時的磁碟I/O

2.資料訪問更快,因為聚簇索引就是表,索引和資料儲存在一個B+Tree中

3.使用索引覆蓋的查詢時可以直接使用頁節點中的主鍵值

聚簇索引的缺點有:

1.插入速度嚴重依賴插入順序

2.更新聚簇索引列的代價很高,因為會強制InnoDB把更新的列移動到新的位置

3.基於聚簇索引的表在插入新行,或者主鍵被更新導致需要移動行的時候,可能會導致“頁分裂”。當行的主鍵值要求必須將這一行插入到已滿的頁中時,儲存引擎會將該頁分裂為兩個頁面來容納該行,這就是一次頁分裂操作,頁分裂會導致表佔用更多的儲存空間。

畫外音:關於,我們在上一篇文章中也提到過。頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁。存和磁碟以頁為單位交換資料。資料庫系統的設計者巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次磁碟I/O就可以完全載入

基於聚簇索引以上的這些特點,在InnoDB中,我們應該儘量使用和應用無關的主鍵,例如自增主鍵,這樣可以保證資料行是按照順序寫入的。而不是使用GUID、UUID生成隨機的主鍵。

向聚簇索引中插入順序的索引值:

每條新紀錄總是在前一條記錄的後面插入:

mark

當頁被插滿後,繼續插入到新的頁:

mark

向聚簇索引中插入隨機的索引值:

新的記錄可能被插入到之前記錄的中間,導致需要強制移動之前的記錄:

mark

被寫滿且已經刷到磁碟上的頁可能會被重新讀取用於再次插入,此時還需要進行頁分裂:

mark

總結

MyISAM和InnoDB兩個儲存引擎的索引雖然都是使用的B+Tree資料結構,但是在具體實現上還是存在不小差別的。InnoDB支援聚簇索引,聚簇索引就是表,所以InnoDB不用像MyISAM那樣需要獨立的行儲存。也就是說,InnoDB的資料檔案本身就是索引檔案。而MyISAM的資料檔案和索引檔案是分開儲存的。可以通過MyISAM和InnoDB如何存放表的抽象圖幫助快速理解。

InnoDB(聚簇)表分佈:

mark

MyISAM(非聚簇)表分佈:

mark

參考

推薦閱讀

MySQL——通過EXPLAIN分析SQL的執行計劃

MySQL——索引基礎

MySQL——索引優化實戰

資料庫索引背後的資料結構

是什麼影響了資料庫索引選型?

mark

相關文章