ibd文件结构组成
页数据格式
InnodDB采用Btree作为存储结构,当用户创建一个Table的时候,就会根据显示或隐式定义的主键构建了一棵Btree,而构成Btree的叶子节点被称为Page,默认大小为16KB,每个Page都有一个独立的Page_no。在我们对数据库中的Table进行修改时,最终产生的影响都是去修改对应TableSpace所对应的Btree上的一个或多个Page。
为了更加方便管理和维护Extent和Page,设置了一些特殊的Page来索引它们,也就是大家常常提起的Page0,Page1,Page2,Page3,从代码的注释来看,各个Page的作用如下:
/* We create a new generic empty tablespace.
We initially let it be 4 pages:
- page 0 is the fsp header and an extent descriptor page,
- page 1 is an ibuf bitmap page,
- page 2 is the first inode page,
- page 3 will contain the root of the clustered index of the
first table we create here. */
(1)Page0即”.ibd”文件的第一个Page,这个Page是在创建一个新的Tablespace的时候初始化,类型为FIL_PAGE_TYPE_FSP_HDR,这个Page用来跟踪后续256个Extent(约256M)的空间管理,所以每隔256M空间大小就需要创建相仿于Page0的Page,这个Page(相仿于page0的page)被称之为Extent的描述页,这个Extent的描述页和Page0除了文件头部信息有些不同外,有着相同的数据结构,且大小都是为16KB,而每个Extent Entry占用40字节,总共分配出了256个Extent Entry,所以Page0和Extent描述页只管理后续256个Extent.区描述符所在页的偏移量都是16384(256 * 64)的倍数。第0页包含有space header信息,后面的区描述符页仅保留该空间。
page0(FSP header page)是表空间的root page,存储表空间关键元数据信息。由page file header、fsp header、xdes entries三大部分构成。完整格式如下图:
FSP_HEADER:
XDES Entry(extent descriptor):
Infimum Record和Supremum Record分别代表该数据页中逻辑最大和最小的值
- FSP_SIZE:表空间大小,以Page数量计算
- FSP_FREE_LIMIT:目前在空闲的Extent上最小的尚未被初始化的Page的`Page Number
- FSP_FREE:空闲extent链表,链表中的每一项为代表extent的xdes,所谓空闲extent是指该extent内所有page均未被使用
- FSP_FREE_FRAG:free frag extent链表,链表中的每一项为代表extent的xdes,所谓free frag extent是指该extent内有部分page未被使用
- FSP_FULL_FRAG:full frag extent链表,链表中的每一项为代表extent的xdes,所谓full frag extent是指该extent内所有Page均已被使用
- FSP_SEG_ID:下次待分配的segment id,每次分配新segment时均会使用该字段作为segment id,并将该字段值+1写回
- FSP_SEG_INODES_FULL:full inode page链表,链表中的每一项为inode page,该链表中的每个inode page内的inode entry都已经被使用
- FSP_SEG_INODES_FREE:free inode page链表,链表中的每一项为inode page,该链表中的每个inode page内上有空闲inode entry可分配
- XDES_BITMAP:占16 字节,每page占2bit,一位表示是否被使用,一位保留。
(2)page1主要用于跟踪随后的每个page的change buffer信息;
(3) page2类型为FIL_PAGE_INODE,存储inode(index node),管理表空间的segment。每个inode对应一个segment。每个inode page默认存储FSP_SEG_INODES_PER_PAGE(85)个inode。预留12个字节来记录前后节点位置
(4)page3也就是第一个索引页,是真正存放用户数据页。在InnoDB中,数据即索引,索引即数据
- PAGE_N_DIR_SLOTS表示数据页中页目录的数量
- PAGE_HEAP_TOP指向数据页中的空闲空间的起始地址
- PAGE_N_HEAP表示目前存放的Record数量
- PAGE_FREE表示删除的Record的链表
- PAGE_GARBAGE表示被删除的Record所占的Bytes大小
- PAGE_LAST_INSERT指向最近一次插入的Record
- PAGE_DIRECTION表示最后一个记录插入的方向
- PAGE_N_DIRECTION表示连续同一个方向插入的Record数量
- PAGE_N_RECS表示当前数据页中用户的Record数量
- PAGE_MAX_TRX_ID表示当前数据页中最大的Transaction ID
- PAGE_LEVEL表示当前数据页在整个索引的B+树中的层级
- PAGE_INDEX_ID表示当前数据页所属的索引ID
- PAGE_BTR_SEG_LEAF表示Leaf节点对应的Segment Header信息
- PAGE_BTR_SEG_TOP表示非Leaf节点对应的Segment Header信息
- PAGE_OLD_INFIMUM表示当前数据页中最大的Record
- PAGE_OLD_SUPREMUM表示当前数据页中最小的Record
数据页的页目录
Page Directory(页目录)占用两个字节,存放了Record在Page的相对位置, 页目录是一个稀疏目录(Sparse Directory), 即一个页目录中有多个Record, 上下限分别为4和8.在整个数据页中,会存在多个Record作为Slot,即管理一个页目录. 其中Record中的REC_NEW_N_OWNED字段记录该页目录的Record数量.
数据页Record的查找
- 调用page_cur_search_with_match()对指定Page的Page Directory(页目录)进行二分查找定位Record周边的两个slot
- 从low_rec开始线性迭代直到up_rec,查找符合条件的Record
总概如下图:
创建表空间
创建表空间最终落实到函数fil_create_tablespace,创建ibd文件并初始化fsp header.
创建segment
innodb的space中,inode、extent和page之间环环相扣,inode对应的是segment,extent 对应的是页簇,page是页。必需先创建segment,然后再从segment内分配extent和page。最终落实到fseg_create_general函数。
分配extent
fsp_alloc_free_extent被用来分配一个extent,即xdes,一般segment内无可用page时会向表空间申请分配空闲extent。分配extent的核心是分配空闲xdes,因为根据xdes我们可以很迅速地定位extent。
分配page
在fseg_alloc_free_page_low函数中实现了7种情况从segment中分配page:
- segment的hint位置的页是空闲状态,直接返回对应的page
- xdes是空闲状态,但segment中的空闲page数量小于总page数量的1/8且碎片页被全部用完,为其分配一个空闲extent,并直接分配hint对应的page
- 如果xdes不是空闲状态,且segment inode中的空闲page数量 < 1/8,在inode当中获得一个空闲的extent,并且将这个extent descr对应的页返回。
- descr是XDES_FSEG状态,且这个extent中还有空闲page,从其中获取一个page.
- 除了以上情况外,如果descr不是空闲的,但是inode还有其他的空闲extent,从其他的extent获得一个空闲
- 如果其他的extent没有空闲页,但是fragment array还有空闲的碎片page,从空闲的碎片page中获得一个空闲页。
7.如果连碎页也没有,直接申请分配一个新的extent,并在其中获取一个空闲的page.
参考借鉴:
MySQL系列:innodb源码分析之表空间管理
InnoDB INODE 页结构
MySQL InnoDB ibd 文件格式解析(fsp header & xdes entry)
https://baijiahao.baidu.com/s?id=1652048495000934560&wfr=spider&for=pc