關(guān)鍵詞
- 表空間、段、區(qū)、頁(yè)、行
- 一個(gè)段256M,一個(gè)區(qū)1M,一個(gè)頁(yè)16K,一個(gè)段=256區(qū)=256*64個(gè)頁(yè)
- 表空間中每連續(xù)256個(gè)區(qū)需要一個(gè)XDES來(lái)維護(hù),XDES里面有256個(gè)entry,每一個(gè)entry描述一個(gè)區(qū)的狀態(tài)、區(qū)里面64個(gè)頁(yè)的狀態(tài)、上一個(gè)和下一個(gè)區(qū)的指針、區(qū)屬于哪一個(gè)段。
- 段分為數(shù)據(jù)段,索引段,回滾段等,在 MySQL中,數(shù)據(jù)是按照B+樹來(lái)存儲(chǔ),因此數(shù)據(jù)即索引,因此數(shù)據(jù)段的頁(yè)即為B+樹的葉子節(jié)點(diǎn),索引段的頁(yè)為B+樹的非葉子節(jié)點(diǎn)。
- Page鏈接起來(lái)就是一個(gè)雙向鏈表的結(jié)構(gòu)
- B+ 樹在查找對(duì)應(yīng)的記錄時(shí),并不會(huì)直接從樹中找出對(duì)應(yīng)的行記錄,它只能獲取記錄所在的頁(yè),將整個(gè)頁(yè)加載到內(nèi)存中,再通過 Page Directory 中存儲(chǔ)的稀疏索引和 n_owned、next_record 屬性取出對(duì)應(yīng)的記錄
https://blog.jcole.us/2013/01/04/page-management-in-innodb-space-files/
https://blog.jcole.us/innodb/
索引組織表
層級(jí)劃分
??InnoDB 實(shí)現(xiàn)的表空間是在文件系統(tǒng)之上又構(gòu)建的一層邏輯存儲(chǔ)的空間管理
??InnoDB 最小讀寫單位是 Page,page 采用默認(rèn)的 16K
??為了高效管理,又將連續(xù)的 64 個(gè)頁(yè)稱為 “extent”,還在 extents 上層添加了一個(gè) segment inode 管理,最多可以管理 256 個(gè) extents
??一個(gè)表空間文件,實(shí)際就是一系列 Pages 的組合 (最大 232),為了方便管理,你可以理解為做了三級(jí)的劃分,分別是 segment、extent、page 。
??段:ibd表空間文件會(huì)被分成多個(gè)段(segment),每個(gè)段和一個(gè)索引關(guān)聯(lián)
??區(qū):段以區(qū)(extent)為大小進(jìn)行增長(zhǎng)或縮小,一個(gè)區(qū)只能屬于一個(gè)段,并且大小為1MB
??頁(yè):頁(yè)(page)是區(qū)的子元素,默認(rèn)大小16KB
??行:一個(gè)頁(yè)可以包含2到N行數(shù)據(jù),這取決與每個(gè)行的大小,但因?yàn)橄拗屏俗钚〈娣?行數(shù)據(jù),所以每一行的大小必須小于8000bytes
??1段 = 256區(qū) = 256*64頁(yè)
查看頁(yè)信息
查看每個(gè)頁(yè)的詳細(xì)信息
innodb_page_info -v dept_emp.ibd
??也可以使用py_innodb_page_info工具
> python py_innodb_page_info.py/usr/local/mysql/data/ibdata1
Total number of page:83584
Insert Buffer Free List:204
Freshly Allocated Page:5467
Undo Log Page:38675
File Segment inode:4
B-tree Node:39233
File Space Header:1
??可以看到共有83 584個(gè)頁(yè),其中插入緩沖的空閑列表有204個(gè)頁(yè)、5467個(gè)可用頁(yè)、38 675個(gè)Undo頁(yè)、39 233個(gè)數(shù)據(jù)頁(yè),等等。你可以通過添加-v參數(shù)來(lái)查看更詳細(xì)的內(nèi)容。
表空間
??表空間需要打開 innodb_file_per_table 變量之后才會(huì)生效
??Page0 肯定是 “File SPace HeaDeR”,其中維護(hù)了 extent 相關(guān)的信息,例如那些是空閑的、滿的、有碎片的 (fragmented)。在 FSP_HDR 頁(yè)中,只能維護(hù) 256 個(gè) extent 相關(guān)的信息 ( 256 extent==16,384 pages==256MiB),這也就導(dǎo)致每隔 256M 都需要有一個(gè) XDES 來(lái)管理接下來(lái)的 extent 元信息。
- File Segment ID:extent屬于哪個(gè)段
- List node for XDES list:指向上一個(gè)和下一個(gè)extent
- State:區(qū)狀態(tài)
- Page State Bitmap:區(qū)里面每一個(gè)頁(yè)的狀態(tài),一個(gè)頁(yè)的狀態(tài)用2個(gè)位表示,一個(gè)區(qū)1M,一個(gè)頁(yè)16K,一個(gè)區(qū)=64個(gè)頁(yè),64 x 2 = 128 bits = 16bytes
段
??表空間是由不同的段組成的,常見的段有:數(shù)據(jù)段,索引段,回滾段等等。
??在 MySQL中,數(shù)據(jù)是按照B+樹來(lái)存儲(chǔ),因此數(shù)據(jù)即索引,因此數(shù)據(jù)段的頁(yè)即為B+樹的葉子節(jié)點(diǎn),索引段的頁(yè)為B+樹的非葉子節(jié)點(diǎn)。
??回滾段用于存儲(chǔ)undo日志,用于事務(wù)失敗后數(shù)據(jù)回滾以及在事務(wù)未提交之前通過undo日志獲取之前版本的數(shù)據(jù),在InnoDB1.1版本之前一個(gè)InnoDB,只支持一個(gè)回滾段,支持1023個(gè)并發(fā)修改事務(wù)同時(shí)進(jìn)行,在InnoDB1.2版本,將回滾段數(shù)量提高到了128個(gè),也就是說(shuō)可以同時(shí)進(jìn)行128*1023個(gè)并發(fā)修改事務(wù)。
??每個(gè)段在開始時(shí),會(huì)根據(jù)數(shù)據(jù)量的需要,逐步增加頁(yè)的數(shù)量,當(dāng)頁(yè)數(shù)量超過32頁(yè)時(shí),會(huì)以64個(gè)頁(yè)為單位,增大頁(yè)數(shù)量。
頁(yè)
innoDB存儲(chǔ)引擎中,常見的頁(yè)類型有:
- 數(shù)據(jù)頁(yè)(B-tree Node)
- undo頁(yè)(undo Log Page)
- 系統(tǒng)頁(yè) (System Page)
- 事物數(shù)據(jù)頁(yè) (Transaction System Page)
- 插入緩沖位圖頁(yè)(Insert Buffer Bitmap)
- 插入緩沖空閑列表頁(yè)(Insert Buffer Free List)
- 未壓縮的二進(jìn)制大對(duì)象頁(yè)(Uncompressed BLOB Page)
- 壓縮的二進(jìn)制大對(duì)象頁(yè) (compressed BLOB Page)
數(shù)據(jù)頁(yè)
??頁(yè)是 InnoDB 存儲(chǔ)引擎管理數(shù)據(jù)的最小磁盤單位,而 B-Tree 節(jié)點(diǎn)就是實(shí)際存放表中數(shù)據(jù)的頁(yè)面。
??在頁(yè)的頭部和尾部之間就是用戶記錄和空閑空間了,每一個(gè)數(shù)據(jù)頁(yè)中都包含 Infimum 和 Supremum 這兩個(gè)虛擬的記錄(可以理解為占位符),Infimum 記錄是比該頁(yè)中任何主鍵值都要小的值,Supremum 是該頁(yè)中的最大值:
??B+ 樹在查找對(duì)應(yīng)的記錄時(shí),并不會(huì)直接從樹中找出對(duì)應(yīng)的行記錄,它只能獲取記錄所在的頁(yè),將整個(gè)頁(yè)加載到內(nèi)存中,再通過 Page Directory 中存儲(chǔ)的稀疏索引和 n_owned、next_record 屬性取出對(duì)應(yīng)的記錄,不過因?yàn)檫@一操作是在內(nèi)存中進(jìn)行的,所以通常會(huì)忽略這部分查找的耗時(shí)。
??InnoDB為每一個(gè)page分配了一個(gè)level級(jí)別:葉子頁(yè)的level是0,隨著樹分支往上走,level不斷遞增。
??所有的非root頁(yè)和非葉子頁(yè)的頁(yè)面,都被稱為internal page。
??無(wú)論是葉子頁(yè)還是非葉子頁(yè), 里面包含的行(record)都有指向下一行的指針(在同一個(gè)page里的偏移量),record鏈表從infimum開始,按鍵升序鏈接所有記錄,終止于supremum。需要注意的時(shí),在page里面的record數(shù)據(jù),從物理角度來(lái)講,在page里面并不是順序存儲(chǔ),行在插入到page時(shí),哪里有空缺空間就會(huì)插入到哪里,所以它們需要依賴的順序只有鏈表上的順序。
行
??InnoDB存儲(chǔ)引擎提供了Compact和Redundant兩種格式來(lái)存放行記錄數(shù)據(jù)。
??SHOW TABLE STATUS LIKE “table_name”,命令可以查看行格式
Compact
- 變長(zhǎng)字段長(zhǎng)度列表:此字段標(biāo)識(shí)列字段的長(zhǎng)度,與列字段順序相反存放,若列長(zhǎng)度小于255字節(jié),用一個(gè)字節(jié)表示,若大于255字節(jié),用兩個(gè)字節(jié)表示,這也是 MySQL的VARCHAR類型最大長(zhǎng)度限制為65535
- NULL標(biāo)志位:標(biāo)識(shí)改列是否有空字段,有用1表示,否則為0,該標(biāo)志位長(zhǎng)度為ceil(N/8)(此處是 MySQL技術(shù)內(nèi)幕-InnoDB存儲(chǔ)引擎與官方文檔有出入的地方);
-
記錄頭信息:固定用5字節(jié)表示,具體含義如下:
- 列數(shù)據(jù):此行存儲(chǔ)著列字段數(shù)據(jù),Null是不占存儲(chǔ)空間的;
- 隱藏列:事務(wù)id和回滾列id,分別占用6、7字節(jié),若此表沒有主鍵,還會(huì)增加6字節(jié)的rowid列。
Redundant
- 字段長(zhǎng)度偏移列表:存儲(chǔ)字段偏移量,與列字段順序相反存放,若列長(zhǎng)度小于255字節(jié),用一個(gè)字節(jié)表示,若大于255字節(jié),用兩個(gè)字節(jié)表示
-
記錄頭信息:固定用6字節(jié)表示,具體含義如下:
- 隱藏列:事務(wù)id和回滾列id,分別占用6、7字節(jié),若此表沒有主鍵,還會(huì)增加6字節(jié)的rowid列。
行溢出
??關(guān)于行溢出,即Redundant格式、Compact格式存儲(chǔ)很長(zhǎng)的字符串,在該字段會(huì)存儲(chǔ)該字符串的前768個(gè)字節(jié)的前綴(字段超過768字節(jié)則為變長(zhǎng)字段),并將整個(gè)字符串存儲(chǔ)在uncompress blob頁(yè)中。
索引
??InnoDB 存儲(chǔ)引擎在絕大多數(shù)情況下使用 B+ 樹建立索引,B+ 樹是平衡樹,它查找任意節(jié)點(diǎn)所耗費(fèi)的時(shí)間都是完全相同的,比較的次數(shù)就是 B+ 樹的高度。當(dāng)數(shù)據(jù)必須從磁盤讀取時(shí),B+樹可以保證最多的讀取次數(shù)(取決于樹的高度)。聚集索引和輔助索引
??數(shù)據(jù)庫(kù)中的 B+ 樹索引可以分為聚集索引(clustered index)和輔助索引(secondary index),它們之間的最大區(qū)別就是,聚集索引中存放著一條行記錄的全部信息,而輔助索引中只包含索引列和一個(gè)用于查找對(duì)應(yīng)行記錄的『書簽』。
聚集索引
??聚集索引就是按照表中主鍵的順序構(gòu)建一顆 B+ 樹,并在葉節(jié)點(diǎn)中存放表中的行記錄數(shù)據(jù)。
??當(dāng)我們使用聚集索引對(duì)表中的數(shù)據(jù)進(jìn)行檢索時(shí),可以直接獲得聚集索引所對(duì)應(yīng)的整條行記錄數(shù)據(jù)所在的頁(yè),不需要進(jìn)行第二次操作。
輔助索引
??數(shù)據(jù)庫(kù)將所有的非聚集索引都劃分為輔助索引,但是這個(gè)概念對(duì)我們理解輔助索引并沒有什么幫助;輔助索引也是通過 B+ 樹實(shí)現(xiàn)的,但是它的葉節(jié)點(diǎn)并不包含行記錄的全部數(shù)據(jù),僅包含索引中的所有鍵和一個(gè)用于查找對(duì)應(yīng)行記錄的『書簽』,在 InnoDB 中這個(gè)書簽就是當(dāng)前記錄的主鍵。