從根兒上理解 MySQL - 頁總結

頁結構

因爲 MySQL 的真實數據是存儲在磁盤, 所以在讀寫數據是會涉及磁盤 IO, 爲了更高效率的讀取, MySQL 設計頁結構, 每次交互以頁爲單位讀取到內存. 頁的大小通常爲 16KBbash

一個數據頁能夠被大體劃分爲7個部分優化

File Header,表示頁的一些通用信息,佔固定的38字節。 Page Header,表示數據頁專有的一些信息,佔固定的56個字節。 Infimum + Supremum,兩個虛擬的僞記錄,分別表示頁中的最小和最大記錄,佔固定的26個字節。 User Records:真實存儲咱們插入的記錄的部分,大小不固定。 Free Space:頁中還沒有使用的部分,大小不肯定。 Page Directory:頁中的某些記錄相對位置,也就是各個槽在頁面中的地址偏移量,大小不固定,插入的記錄越多,這個部分佔用的空間越多。 File Trailer:用於檢驗頁是否完整的部分,佔用固定的8個字節。spa

image-20200129175558772

image-20200129175624645

上述的行格式即爲 User Records的一部分設計

新生成的頁並無 User Records, 當插入數據時, 會從 Free Space 申請, 若是 Free Space 的空間所有被替換掉, 則說明該頁滿了, 須要申請新的頁3d

Page Directory (頁目錄)

image-20200129180219930

從 select * from table wher id = 1 講起 比較笨的方式會從最小記錄開始遍歷往下找, 知道找到 next_record = 0指針

MySQL 的優化點在於新增了一個目錄, 利用二分查找優化code

  1. 經過二分法肯定該記錄所在的槽(實際是找到肯定的槽, 遍歷該槽的上一個槽或者當前槽)
  2. 經過記錄的next_record屬性遍歷該槽所在的組中的各個記錄

image-20200129180437808

由於把16條記錄的所有信息都畫在一張圖裏太佔地方,讓人眼花繚亂的,因此只保留了用戶記錄頭信息中的n_owned和next_record屬性,也省略了各個記錄之間的箭頭,我沒畫不等於沒有啊!如今看怎麼從這個頁目錄中查找記錄。由於各個槽表明的記錄的主鍵值都是從小到大排序的,因此咱們可使用所謂的二分法來進行快速查找。5個槽的編號分別是:0、一、二、三、4,因此初始狀況下最低的槽就是low=0,最高的槽就是high=4。比方說咱們想找主鍵值爲6的記錄,過程是這樣的:

計算中間槽的位置:(0+4)/2=2,因此查看槽2對應記錄的主鍵值爲8,又由於8 > 6,因此設置high=2,low保持不變。

從新計算中間槽的位置:(0+2)/2=1,因此查看槽1對應的主鍵值爲4,又由於4 < 6,因此設置low=1,high保持不變。

由於high - low的值爲1,因此肯定主鍵值爲6的記錄在槽2對應的組中。此刻咱們須要找到槽2中主鍵值最小的那條記錄,而後沿着單向鏈表遍歷槽2中的記錄。可是咱們前邊又說過,每一個槽對應的記錄都是該組中主鍵值最大的記錄,這裏槽2對應的記錄是主鍵值爲8的記錄,怎麼定位一個組中最小的記錄呢?別忘了各個槽都是挨着的,咱們能夠很輕易的拿到槽1對應的記錄(主鍵值爲4),該條記錄的下一條記錄就是槽2中主鍵值最小的記錄,該記錄的主鍵值爲5。因此咱們能夠從這條主鍵值爲5的記錄出發,遍歷槽2中的各條記錄,直到找到主鍵值爲6的那條記錄便可。因爲一個組中包含的記錄條數只能是1~8條,因此遍歷一個組中的記錄的代價是很小的。

因此在一個數據頁中查找指定主鍵值的記錄的過程分爲兩步:

經過二分法肯定該記錄所在的槽,並找到該槽所在分組中主鍵值最小的那條記錄。

經過記錄的next_record屬性遍歷該槽所在的組中的各個記錄。
複製代碼

行格式

行格式主要分爲四種類型CompactRedundantDynamicCompressed. 主要理解 Compactcdn

Compact 行格式

image-20200129145838600

行格式主要分爲記錄的額外信息, 記錄的真實數據blog

變長字段長度列表

變長字段長度列表存儲的是變長類型的真實數據的佔用字節數(逆序).排序

若是該表沒有變長類型, 則無變長字段長度列表

變長字段長度列表只保存 NOT NULL列, 若是該列容許爲 NULL, 則不會保存

舉例: 字段 0 int 0, 字段1 vachar(10) NOT NULL 'A', 字段2 vachar(10) default NULL 'B', 字段3 vachar(10) NOT NULL 'AA'
變長字段長度列表: 02 01
可經過 變長字段反推字段的長度
複製代碼

NULL 值列表

NULL 值列表只統計哪些字段容許爲 NULL的值狀態(逆序)

若是該表沒有容許 NULL 列, 則無 NULL 值列表

MySQL規定NULL值列表必須用整數個字節的位表示,若是使用的二進制位個數不是整數個字節,則在字節的高位補0

二進制位的值爲1時,表明該列的值爲NULL 二進制位的值爲0時,表明該列的值不爲NULL

舉例: 字段 0 vachar(10) NOT NULL 'A', 字段1 vachar(10) default NULL 'B', 字段2 vachar(10) default NULL NULL, 字段3 vachar(10) default NULL NULL
逆序後: 字段3 - 1, 字段2 - 1, 字段1 - 0
NULL 值列表: 00000110 
可經過 NOT NULL 字段反推哪些字段值爲 NULL, 哪些字段值不爲 NULL
複製代碼

記錄頭

這一部分牽扯的內容較多

大體能夠看一下刪除標識(刪除是並不是真正刪除, 只是修改表示), 下一條記錄位置(B+樹特性)

image-20200129152146891

image-20200129152534179

除了這些詳細信息, 還會有 MySQL 自動添加的隱藏列

實際上這幾個列的真正名稱實際上是:DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR, 爲了美觀才寫成了row_id、transaction_id和roll_pointer

row_id: 有主鍵使用主鍵, 沒有主鍵有惟一鍵使用惟一鍵, 沒有則自動生成

transaction_id: 事務 ID

roll_pointer: 回滾指針

image-20200129152851500

行溢出

由於一個頁佔用 16KB, 16 * 1024 = 16384 字節, 而 varchar 最多能夠佔用65535, 不包括隱藏列和記錄頭信息

可是在咱們使用 varchar 時, 會設置變長字段長度列表和 NULL 值列表

對於 NOT NULL, 只能使用65533字節 (兩個字節用來表示長度) 對於非 NOT NULL, 只用使用 65532 字節 (兩個字節用來表示長度, 一個字節用來表示 NULL 標識)

image-20200129154058216

對於行溢出的狀況, 真實的數據只用存儲前 768 字節, 後面放的是溢出頁地址
複製代碼

行溢出的節點

綜上所述, 咱們知道在數據的佔用字節數超過某個閾值就會發生行溢出, 那麼行溢出的計算方式分析以下

ps: 截圖來源於MySQL 是怎樣運行的:從根兒上理解 MySQL(小孩子 4919)

image-20200129154804902

其餘

delete_mask

這個屬性標識該條記錄是否已刪除

若是 MySQL 設計刪除一條則執行磁盤刪除, 會致使磁盤IO增長, MySQL 爲了優化, 使用一個標識表示該記錄已被刪除, 若是有新的記錄來, 會覆蓋該條記錄 原理: 刪除數據時, 標識該記錄已刪除, 而且放入垃圾鏈表, 新的記錄插入時, 先從垃圾鏈表中替換

若是想要優化空間, 則執行 optimize table 'name' 便可

參考文獻

MySQL 是怎樣運行的:從根兒上理解 MySQL(記錄結構) MySQL 是怎樣運行的:從根兒上理解 MySQL(數據頁結構)

相關文章
相關標籤/搜索