innodb 数据页
innodb 数据页
在前面一系列介绍innodb文章里面多次提到数据页(page)的概念,鉴于数据页是底层ibd最小划分单位,表空间文件就是由一个又一个的页构成,因此页的重要性也就不言而喻,现在我们来具体分析innodb数据页的构成
表空间文件在物理上以16KB为一个步进,Mysql磁盘IO的最小单位,回顾一下整体的表空间文件逻辑结构
最底层的ibd物理文件按照16kb的维度划分为一个一个数据页(此处分析不包含page=0,1,2)等元数据信息控制页,想了解这部分内容的可以参考之前介绍的关于段、区的文章段、区描述
照例来看看基础的索引页结构
- FIL Header (38) 通常称之为文件头,这个所有的数据页均有此部分
- Index Header(36) 通常称之为页头
- Fseg Header(20) 段头,仅在索引页的根节点中设置
- System Records(26) 系统记录(infimum/supremum)
- User Records 用户记录
- Free Spaces 空闲空间
- Page Directory 页面目录槽
- FIL Trailer(8) 文件尾部
FIL HEADER/Trailer结构图
- Checksum (4) 校验码
- Offset (Page Number) (4) 页号,亦即PageNo
- Previous Page (4) 前一页的页号
- Next Page (4) 后一页的页号
- LSN for last page modification (8) 最近一次修改page的lsn号
- Page Type (2) 数据页类型
- Flush LSN (0 except space 0 page 0) (8) 只用于系统表空间的第一个Page,记录在正常shutdown时安全checkpoint到的点,对于用户表空间,这个字段通常是空闲的
- Space ID (4) 表空间ID
Fil Trailer
- Old-style Checksum (4) checksum,用于与header部分的checksum配合工作,检查数据页的完整性
- Low 32 bits of LSN (4) LSN的低位置4字节
Index Header结构
- Number of Directory Slots (2) Page directory中的槽slot个数
- Heap Top Position (2) 指向当前Page内已使用的空间的末尾位置,即free space的开始位置
- Number of Heap Records / Format Flag (2) Page内所有记录个数(包含删除的),当第一个bit设置为1时,表示这个page内是以Compact格式存储的
- First Garbage Record Offset (2) 指向标记删除的记录链表的第一个记录
- Garbage Space (2) 被删除的记录链表上占用的总的字节数
- Last Insert Position (2) 指向最近一次插入的记录偏移量,主要用于优化顺序插入操作
- Page Direction (2) 用于指示当前记录的插入顺序以及是否正在进行顺序插入,插入时,PAGE_LAST_INSERT会和当前记录进行比较,以确认插入方向,据此进行插入优化
- Number of Inserts in Page Direction (2) 以相同方向的顺序插入记录个数
- Number of Records (2) Page上有效的未被标记删除的用户记录个数
- Maximum Transaction ID (8) 最近一次修改该page记录的事务ID
- Page Level (2) 该Page所在的btree level,根节点的level最大,叶子节点的level为0,非叶子节点以树形结构向上增长
- Index ID (8) 该Page归属的索引ID
Fseg Header 结构
- Leaf Pages Inode Space ID (4) 叶子节点的表空间ID
- Leaf Pages Inode Page Number (4) 叶子节点索引段的页号
- Leaf Pages Inode Offset (2) 叶子节点索引段entry偏移量(标示具体的inode segment entry 偏移)
- Internal (non-leaf) Inode Space ID (4) 非叶子节点的表空间ID
- Internal (non-leaf) Inode Page Number (4) 非叶子节点索引段的页号
- Internal (non-leaf) Inode Offset (2) 非叶子节点索引段entry偏移量(标示具体的inode segment entry 偏移)
此部分内容值存在于数据页level是根节点(索引根节点)的数据页内
System Records 结构
- Info Flags (4 bits) 标记信息
- Number of Records Owned (4 bits) m_owned值,对于infimum 此值固定为1,下面介绍page slots会解释为什么是1
- Order (13 bits) 序号,一说为heap_number
- Record Type (3 bits) 记录类型
- Next Record Offset (2) 下一个记录的页内偏移位置
- "infimum\0" (8) 固定"infimum"值
- Info Flags (4 bits) 标记信息
- Number of Records Owned (4 bits) m_owned值,对于supremum取值范围为1-8,
- Order (13 bits) 序号
- Record Type (3 bits) 记录类型
- Next Record Offset (2) 下一个记录的页内偏移位置
- "supremum" (8) 固定"supremum"值
系统记录由innodb自动生成,无需开发人员干预
-
infimum代表当前页比最小记录还小的记录(用作链表的表头)
-
supremum代表当前页比最大记录还大的记录(用作链表的表尾)
此处"最小/最大"记录都是相对于当前页来说的,并不是全局数据节点层面的最大最小记录,主要用于页内数据链表表头/表尾起始结束位置的比对,举个例子,当查询一个id不存在的数据项目时候,load数据页进内存,在内存里面使用2分法查找,找到infimum/supremum记录仍然没有找到指定id记录的时候,此时需要结束查找过程,返回id数据不存在的结果
User Records 用户记录/Free Space
这部分记录即为真实的用户记录存放区域/空闲空间
Page Directory 页面目录槽
前面提到过slots以及owned的概念,之前文章也提到过页面内容的查找是在内存中完成的,内存查找的方法有很多,常见的链表顺序遍历、有序数据结构的二分查找算法、hash查找,目录槽是一个稀疏数组,之前的文章提到过目录槽结构的命令
innodb_space -s /usr/local/var/mysql/ibdata1 -T test/user_info2 -p 6 page-directory-summary
输出:
slot offset type owned key
0 99 infimum 1
1 395 conventional 4 (id=89)
2 751 conventional 4 (id=93)
3 1107 conventional 4 (id=97)
4 1463 conventional 4 (id=101)
5 1819 conventional 4 (id=105)
6 2175 conventional 4 (id=109)
7 2531 conventional 4 (id=113)
8 2887 conventional 4 (id=117)
9 3243 conventional 4 (id=121)
10 3599 conventional 4 (id=125)
11 3955 conventional 4 (id=129)
12 4311 conventional 4 (id=133)
13 4667 conventional 4 (id=137)
14 5023 conventional 4 (id=141)
15 5379 conventional 4 (id=145)
16 5735 conventional 4 (id=149)
17 6091 conventional 4 (id=153)
18 6447 conventional 4 (id=157)
19 6803 conventional 4 (id=161)
20 7159 conventional 4 (id=165)
21 7515 conventional 4 (id=169)
22 7871 conventional 4 (id=173)
23 8227 conventional 4 (id=177)
24 8583 conventional 4 (id=181)
25 8939 conventional 4 (id=185)
26 9295 conventional 4 (id=189)
27 9651 conventional 4 (id=193)
28 10007 conventional 4 (id=197)
29 10363 conventional 4 (id=201)
30 10719 conventional 4 (id=205)
31 11075 conventional 4 (id=209)
32 11431 conventional 4 (id=213)
33 11787 conventional 4 (id=217)
34 12143 conventional 4 (id=221)
35 12499 conventional 4 (id=225)
36 12855 conventional 4 (id=229)
37 13211 conventional 4 (id=233)
38 13567 conventional 4 (id=237)
39 13923 conventional 4 (id=241)
40 14279 conventional 4 (id=245)
41 14635 conventional 4 (id=249)
42 112 supremum 7
这是pageno=6的数据页槽内容
下面这个可以查看pageno=6的数据页内容
innodb_space -s /usr/local/var/mysql/ibdata1 -T test/user_info2 -p 6 page-records
输出,内容较多,此处只列出了部分内容(含头、尾数据):
Record 128: (id=86) → (name="c6u0olu1t99bintxuwju", phone="00140889180", email="unmg3kktfcfaybsclinz", create_time="184913571-04-58 64:37:75", update_time="1997-02-26 10:06:12")
Record 217: (id=87) → (name="b6zlukjvcijclmtsm61c", phone="73416452518", email="fxtqhwklqfvri0wximdg", create_time="184913571-04-92 19:82:07", update_time="1998-03-21 18:46:44")
Record 306: (id=88) → (name="lwoh05cclf8x5i8mg2g4", phone="23970977406", email="cmopy6gxavhwnbbhsiwh", create_time="184913571-05-25 75:26:39", update_time="1999-04-14 03:27:16")
Record 395: (id=89) → (name="s3hesywuyuiifugxufhk", phone="27097768320", email="ta3nac9mgrjp1s5vbx7x", create_time="184913571-05-59 30:70:71", update_time="2000-05-06 12:07:48")
Record 484: (id=90) → (name="cchc4lnf4dfybc2qpk52", phone="64290362245", email="zyrwthg7pxjnqjuda1r0", create_time="184913571-05-92 86:15:03", update_time="2001-05-29 20:48:20")
Record 573: (id=91) → (name="xvl8gicmu3ls3kperbdk", phone="68101472060", email="b6apbvpuoopi9rasmjio", create_time="184913571-06-26 41:59:35", update_time="2002-06-22 05:28:52")
Record 662: (id=92) → (name="t7kfqinv3nyssli3dipm", phone="17411298527", email="rwrbhq8ghahwn9vrfako", create_time="184913571-06-59 97:03:67", update_time="2003-07-15 14:09:24")
Record 751: (id=93) → (name="huflr7sogkmuye4va97y", phone="25370775440", email="x29tkcz5zrk9crfkplan", create_time="184913571-06-93 52:47:99", update_time="2004-08-06 22:49:56")
Record 840: (id=94) → (name="dnbl0r6ntriomk8vye1i", phone="00944807720", email="qkygjoppuqx1kqxot6jb", create_time="184913571-07-27 07:92:31", update_time="2005-08-30 07:30:28")
....
Record 14546: (id=248) → (name="bgf9zgtnjhjzjncarogy", phone="73579774944", email="xo5pj2mgtfo3wmqzf5xg", create_time="184913571-62-97 11:49:44", update_time="2033-04-22 17:04:20")
Record 14635: (id=249) → (name="yodufhkpu7i57akwb18t", phone="61058341505", email="wljt1vkznox6i7gpdrno", create_time="184913571-63-30 66:93:76", update_time="2034-05-16 01:44:52")
Record 14724: (id=250) → (name="hay9tqm9wx5pcht6weca", phone="83173571461", email="ygjmimuafb3klyb9px1l", create_time="184913571-63-64 22:38:08", update_time="2035-06-08 10:25:24")
Record 14813: (id=251) → (name="tdvgmgsdjiwfzwo7xgjz", phone="69997944082", email="bjf0pxbum1e1yfe1f1co", create_time="184913571-63-97 77:82:40", update_time="2036-06-30 19:05:56")
Record 14902: (id=252) → (name="fhyvivjbvgudqmnbknx7", phone="77376965702", email="4zcwekwqqh5aolmahcn0", create_time="184913571-64-31 33:26:72", update_time="2037-07-24 03:46:28")
Record 14991: (id=253) → (name="tdrqgvp9k231kxgxm62i", phone="11097938259", email="wzf5xgawn1wqmpnao3ud", create_time="184913571-64-64 88:71:04", update_time="2038-08-16 12:27:00")
Record 15080: (id=254) → (name="cdgzudipzxbd8qhnj57a", phone="16963982701", email="zpfa4pg1vp68extfvoh2", create_time="184913571-64-98 44:15:36", update_time="2039-09-08 21:07:32")
Record 15169: (id=255) → (name="bexa9qcpzzlt3pjgyto7", phone="17514550564", email="fjlf6mkr3ogq1omxba5p", create_time="184913571-65-31 99:59:68", update_time="2040-09-16 00:55:28")
-
可以看到infimum的 owned 值为1,因为infimum前面没有数据记录, infimum为第一条记录,owned只包含它自己
-
id=89 的槽owned值为4,我们看记录可以知道,它前面的数据有id=86,87,88,算上自己89,总共四个值
-
supremum 的owned值7,我们知道上一个槽的key值为249,从记录列表里面可以看到supremum之前有id=250,251,252,253,254,255 共六条记录,算上自己一共七条记录
数据槽/记录的逻辑结构
可以看到底层数据行之间顺序关系是链式结构,目录槽记录的是offset(页内偏移),key值为主键ID(对于主索引来说,对于辅助索引的节点key值为对应的辅助索引内容),顺带提一下,数据槽的内容按照offset中对应的key值有序,而非offset排序
综上,owned值将页内数据id按照一定的规则,划分为一个一个的区块,构成一个稀疏的数组,每个owned的区间描述是"(]"前开后闭形式的,描述在此之前有多少个记录,页内数据查找先根据槽信息作2分查找,然后通过链表遍历的方式在区间内做数据最后一层的查找定位