MySQL数据库Innodb储存引擎----储存页的结构
上一篇博客回顧:
1:數據庫擁有眾多的儲存引擎,現在主要使用的是Inoodb,這個儲存引擎有Compact,Redundant,Dynamic,Compressed四種行格式
2:Compact行格式的結構分為變長數據長度列表,NULL值列表,記錄頭信息,真是數據儲存
3:變長數據長度列表儲存的是變長數據類型數據的字節數逆順序,空值列不儲存,NULL值列表儲存非主鍵和沒有被NOT? NULL?修飾的列,二進制位逆順序進行儲存。
4:記錄頭信息包括了偏移量,槽數量,本組數據量,是否被刪除,數據類型,是不是B+樹子節點等等信息。
5:真實數據會有三個三個虛擬列,ROW_ID(沒有主鍵的時候自動生成),ROLL_POINTER,TRANSACTION_ID(事務管理ID)
5:行溢出數據的處理Compact行格式是使用最后記錄下一頁地址的方式,然而Redundant和Compressed是采用整頁記錄數據頁地址的方式,后兩者的Compressed采用壓縮算法。
6:對于char相似類型的數據來說,如果我們采用可變的字符集進行操作也是會在可變長度數據列表里面進行儲存的。
7:對于可變數據長度列表儲存的占用字節為1或者2,NULL值用二進制位,記錄頭信息在Compact行格式占用5個字節,Redundant占用6字節。一頁空間16kb。
頁儲存結構:
我們都知道的是數據庫一個頁的儲存空間是16kb,那么這16kb的儲存空間是怎么進行分配,數據在這個儲存空間里是怎么樣的一個格式呢?對于這些數據數據庫都進行了什么操作?這就是我們今天需要進行學習的內容。
可能按照順序來說的話不是太方便進行講訴,而且看起來可能也不會效果太好,所以我們一點一點根據功能的劃分進行區分。
User Records:
這個區域對我們插入的數據進行保存,需要說明的是原本這一塊是不存在的,當我們插入數據的時候這塊區域才會被劃分出來。并且是從Free Space進行的劃分。當我們的Free Space區域所有的空間都變成了UserRecords,那么這時候就是需要重新開辟一個儲存頁的時間了。
那么當我們把數據放在這個區域里面的時候,是不是就是說沒有一點規則,隨便進行擺放,其實想一想就知道了,當我們數據過于龐大的時候,我們隨便擺放,查找會是多么的痛苦。所以接下來在我們知道數據保存在那個位置以后我們需要弄清楚的是數據在User Records里的情況。在這里我們假設插入了四條我們自己的記錄:
兩個虛擬的數據:我們可以看到下圖所示,我們插入了四條記錄的時候,但是在我們這個頁中存在的是六條記錄,也就是兩條我們說的每個頁中都會存在的虛擬記錄:最大記錄和最小記錄。他們都存infumum_supremum里面,因為不是我們自己插入的記錄所以是不在User_Record里面.最小記錄默認在開始,最大記錄在最末尾結束
接下來我們再根據這六條記錄描述一些問題:
1:我們在每條數據的記錄頭里面提到的Record_Type標記的是這條數據的類型,當時我們說的是有0普通數據,1葉子節點數據,2最小數據,3最大數據。我們可以看到的是上邊最大和 最小數據分別是3和2, 我們自己插入數據的記錄頭信息在Record_type這里都是0.
2:我們在記錄頭信息里面還可以看到的是delete_mask這個數據,表示的是數據是否被刪除,0表示沒有,1表示已經被刪除,所以上邊的數據都是0
3:heap_no我們講過是標記該數據在頁中的位置,我們可以看到插入數據分別是2,3,4,5。那么0和1去哪了,別著急,請看看最小記錄和最大記錄的該數據,是不是分別為0和1。我 們插入的數據都會從2開始計數,虛擬數據會占用默認的0和1的位置。
4:插入數據的排列是否就是數據插入的順序,那顯然是不可能的,你沒想錯,數據會根據大小進行排列,那么數據用什么進行大小的排列?顯然就是主鍵進行比較。
5:next_record記錄的就是相對于本條數據,下一條數據的地址偏移量,就是通過這條數據往下查找這么多字節就可以找到下一條數據,沒錯。他就是使用的鏈表進行鏈接的。如下圖:
?
? 6:如果一條數據被刪除,也就是它的delete_mask被標記為了1,那么這個會怎么進行改變?就是和鏈表一致,進行鏈接的切斷就可以了。
7:我們在進行數據查找的時候就是這么一條接一條的進行查找么?從最小記錄開始根據next_record查找?那必然是耗時的一個活,顯然是不可能的,所以在就出現了分組這個概念。
?
?
? 分組:
我們可以看到的是六條數據分成了兩組,首先是最小的虛擬數據獨自一組,然后剩下的五個數據再分成一組。在這里需要知道的就是MySql數據庫在每個頁中進行數據分組的時候默認的 最小數據是第一組,它擁有一條數據,就是最小數據,不能在插入其他數據。最大數據是第二組,我們在進行數據插入的時候都是先插入最大數據組,當最大數據組滿足的時候進行分裂,形 成普通的分組,然后再進來的數據又插入最大數據組,如此循環往復,完成數據的分組。
槽:
我們還可以看到的是在分組的圖里面出現了兩個奇奇怪怪的東西,槽,我們上一篇文章頁訴說過這個玩意兒。每個分組數據的相對于頁的地址偏移量就是一個槽數據,一個分組有一個 槽,槽存在的位置就是頁信息的Page Directory。在這里我需要強調的是在記錄頭信息中有個地址偏移量next_record,這個偏移量是本條數據相對于下一條數據位置,然后槽中的偏移量是分 組最后一條數據相對于頁的偏移量。
尋找:
有了分組以后,我們在進行數據查找的時候就是根據二分法確定對應數據所在的槽位置,然后在使用記錄頭信息的next_record一條條進行查找。
n_owned:
這個數據我們在記錄頭信息中一直看到,其實在這里就可以結束這個數據了。它表示的是該分組有多少條數據,存在于分組的最后一條信息中。我們可以看到的是每個分組的前面的數據 n_owned都是0,只有在最后一條數據上它才有值。
page_header
? 上邊我們通過數據的方式介紹了User_Records,infumum_supremum,page_directory,Free spce這四塊空間的使用情況,接下來需要進行解釋的就是page_header,file_header,file tailer這三塊空間。
首先說的就是page_header,這個地方儲存的就是數據的一些信息:
| PAGE_N_DIR_SLOTS | 2字節 | 在頁目錄中的槽數量 |
| PAGE_HEAP_TOP | 2字節 | 第一個記錄的地址 |
| PAGE_N_HEAP | 2字節 | 本頁中的記錄的數量(包括最小和最大記錄以及標記為刪除的記錄) |
| PAGE_FREE | 2字節 | 指向可重用空間的地址(就是標記為刪除的記錄地址) |
| PAGE_GARBAGE | 2字節 | 已刪除的字節數,行記錄結構中delete_flag為1的記錄大小總數 |
| PAGE_LAST_INSERT | 2字節 | 最后插入記錄的位置 |
| PAGE_DIRECTION | 2字節 | 最后插入的方向 |
| PAGE_N_DIRECTION | 2字節 | 一個方向連續插入的記錄數量 |
| PAGE_N_RECS | 2字節 | 該頁中記錄的數量(不包括最小和最大記錄以及被標記為刪除的記錄) |
| PAGE_MAX_TRX_ID | 8字節 | 修改當前頁的最大事務ID,該值僅在二級索引中定義 |
| PAGE_LEVEL | 2字節 | 當前頁在索引樹中的位置,高度 |
| PAGE_INDEX_ID | 8字節 | 索引ID,表示當前頁屬于哪個索引 |
| PAGE_BTR | 10字節 | 非葉節點所在段的segment header,僅在B+樹的Root頁定義 |
| PAGE_LEVEL | 10字節 | B+樹所在段的segment header,僅在B+樹的Root頁定義 |
? 在上邊我們需要說的就是PAGE_DIRECTION和PAGE_N_RECS這兩個數據第一個指的是最后插入的方向,相對于上一條數據來說,我們新插入的比他大,就在右邊,反之則在左邊,這就是方 向。當我們插入數據連續的都在右邊或者是都在左邊的時候就會記錄下數量。當然如果改變方向的話這個數據會被清空從零開始計數。
File_Header:
上邊講的page_header就是對頁儲存記錄的描述,那么這里的File_Header就是對頁信息的描述:
| FIL_PAGE_SPACE_OR_CHKSUM | 4字節 | 頁的校驗和(checksum值) |
| FIL_PAGE_OFFSET | 4字節 | 頁號 |
| FIL_PAGE_PREV | 4字節 | 上一個頁的頁號 |
| FIL_PAGE_NEXT | 4字節 | 下一個頁的頁號 |
| FIL_PAGE_LSN | 8字節 | 最后被修改的日志序列位置(英文名是:Log Sequence Number) |
| FIL_PAGE_TYPE | 2字節 | 該頁的類型 |
| FIL_PAGE_FILE_FLUSH_LSN | 8字節 | 僅在系統表空間的一個頁中定義,代表文件至少被更新到了該LSN值,獨立表空間中都是0 |
| FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4字節 | 頁屬于哪個表空間 |
?
? 我們可以看到三個值,一個記錄的是這個頁的頁號,上一個頁,下一個頁。針對上面我們講的一個頁中的數據是采用單向鏈表的形式進行連接,那么我們可以想到的是數據庫中的頁是采用的雙向鏈表。
我們在上面還可以看到的是 FIL_PAGE_TYPE這個值,描述的是這個頁的類型,顯然我們數據庫不可能就只有一種數據頁,上面我們講的儲存真實數據頁就是數據頁。FIL_PAGE_INDEX,也就是我們提到的 B+樹葉子節點。
| FIL_PAGE_ALLOCATED | 0x0000 | 最新分配,還沒使用 |
| FIL_PAGE_UNDO_LOG | 0x0002 | Undo Log頁 |
| FIL_PAGE_INODE | 0x0003 | 段信息的節點 |
| FIL_PAGE_IBUUF_FRE_LIST | 0x0004 | Insert Buffer空閑列表 |
| FIL_PAGE_IBUF_BITMAP | 0x0005 | Insert Buffer位圖 |
| FIL_PAGE_TYPE_SYS | 0x0006 | 系統頁 |
| FIL_PAGE_TYPE_TRX_SYS | 0x0007 | 事務系統數據 |
| FIL_PAGE_TYPE_FSP_HDR | 0x0008 | File Space Header |
| FIL_PAGE_TYPE_XDES | 0x0009 | 擴展描述頁 |
| FIL_PAGE_TYPE_BLOB | 0x000A | BLOB頁 |
| FIL_PAGE_INDEX | 0x45BF | B+樹的子節點 |
?File Trailer
? 這玩意需要和上面File header中的FIL_PAGE_SPACE_OR_CHKSUM這個屬性系統校驗和一起說。我們都知道的是頁是一塊16kb的儲存空間,不管是內存刷新到數據庫取用都是一次操作16kb。那么如果在中途產生停電等不可抗拒的因素,這時候這里就起作用了。File Header位于頁的開始,它會計算一個校驗和,這個校驗和你可以這么理解,當我們需要一個很復雜的字符串的時候,往往會將它按照一定的算法進行計算出一個整數值,當和其它字符串進行比較的時候就用這個值。所以校驗和也是這個道理,File Trailer是位于頁尾部的,他也會儲存一個校驗和。如果數據不完整,那么兩個校驗和不可能一致,那么就可以判定這個數據頁是損壞的。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/zslli/p/8888285.html
總結
以上是生活随笔為你收集整理的MySQL数据库Innodb储存引擎----储存页的结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么理解腾讯元宝的底层技术原理?
- 下一篇: 学习MySQL我们应该知道哪些东西?