InnoDB原理篇:聊聊數據頁變成索引這件事
數據頁
我們都知道平時執行crud的時候,都會從磁盤上加載數據頁到Buffer Pool的緩存頁里去,更新緩存頁后,由異步線程刷回磁盤的數據頁。
所以MySQL進行數據操作的最小單位是數據頁,接下來就分析分析,數據頁到底長什么樣。
每個數據頁默認16kb的大小,數據頁由多個部分組成,如下圖所示:
當然這么多概念,阿星只會挑重點,循循漸進的講,所以大家莫慌。
空閑空間
其實數據頁還未寫入數據時,是沒有數據行的,只有空閑空間,一旦寫入,空閑空間會減少一些,直到空閑空間耗盡,具體過程如下圖:
數據頁滿了,自然需要開辟新的數據頁出來存儲數據。
但是隨著數據頁多起來,它們怎么知道上一頁與下一頁在那呢?
雙向鏈表
其實在數據頁文件頭中存放了特別多的信息,如當前頁號、頁類型、所屬表空間、上一頁號、下一頁號等等。
所以數據頁是通過上下頁號,組成雙向鏈表,如下圖所示:
數據頁內部會存儲一行一行的數據,每一行數據都會按照主鍵大小進行排序存儲,同時每一行數據都有指針指向下一行數據,組成單向鏈表。
但是這個結構并不高效,假設根據主鍵ID查詢數據,只能進入數據頁,挨個挨個的對單向鏈表遍歷查詢。
所以要再加點料,把二分查找利用起來(不知道二分查找是什么,建議百度,這是最基礎算法)
數據頁目錄
這個料就是數據頁目錄部分,數據頁目錄存儲的內容就是主鍵ID和行位置。
這樣就可以通過數據頁目錄走二分查找,快速定位到數據頁內的數據行。
如果只有一個數據頁,倒沒啥問題,哪有成千上萬個數據頁呢,還是得一個一個進數據頁,搜索數據頁目錄。
有沒有覺得,這似乎是在做全表掃描?
沒錯,在沒有索引的情況下,數據庫就是這樣執行的。
索引
如果沒有索引,查詢速度可以說是慢到驚人,一般是不能讓查詢走全表掃描的。
因此數據庫中的查詢,必須要運用索引來加速。
頁分裂
在說索引之前,先說個前置知識,索引的核心基礎要求后一個數據頁的主鍵值都大于前面一個數據頁的主鍵值,如果你的主鍵是自增的,可以保證這一點。
但有時候主鍵并不是自增長的,可能會出現后一個數據頁的主鍵值小于前一個數據頁的主鍵值。
為了保證索引的核心基礎,有個交換行數據的過程,這個過程叫頁分裂。
過程如下
- 數據頁0的id=6行數據挪到數據頁1
- 數據頁1的頁目錄更新
- 數據頁1的id=3行數據挪到數據頁0
- 數據頁0的頁目錄更新
主鍵目錄
好了,現在我們以主鍵為例,創建一個主鍵索引,這個主鍵索引就是主鍵目錄,它會維護所有數據頁的最小主鍵值與對應的頁號。
有了主鍵目錄的加持,那找數據就非常快了,過程如下
- 二分查找主鍵目錄,找到對應的數據頁
- 進入數據頁,二分查找數據頁目錄,找到對應的行數據
可是又來一個新問題,表里的數據可能有幾百萬,幾千萬,甚至幾億條數據,會有大量的數據頁,意味著主鍵目錄要存儲大量的數據頁號和最小主鍵值。
可能主鍵目錄存儲不下,就算能存儲,海量的數據僅僅靠二分查找也很吃力。
所以InnoDB實際上是把主鍵目錄數據存儲在多個數據頁中,我們把這個數據頁稱為索引頁
索引頁
索引頁,顧名思義,就是存儲索引信息的數據頁,在數據頁的文件頭部,有頁類型來進行區分。
索引頁會存儲兩類內容,一類是最小主鍵值與索引頁號,另一類是最小主鍵值與數據頁號。
把大量的索引信息分散在多個索引頁中,再將多個索引頁組建成B+樹結構,方便二分查找,結構如下圖:
一直說InnoDB的索引是用B+樹來組成的,其實就是這個意思,當然真實的B+樹不長這樣,這樣畫還是為了幫助大家理解。
現在整個搜索過程就十分簡單了
- 根據主鍵id二分查找索引頁
- 找到對應索引頁,再二分查找數據頁
- 進入數據頁,二分查找數據頁目錄,找到對應的行數據
寫到這里就結束了,通過數據頁到最后的索引,體會這個優化的過程,才是本文的重點,由于篇幅有限,索引的內容后面會單獨講解。