表空间文件组成结构
InnoDb存储引擎会初始化一个名为ibdata1的表空间文件,默认会存储所有表的数据,以及我们所熟知但看不到的系统表SYS_TABLES,SYS_COLUMNS,SYS_INDEXES,SYS_FIELDS等。
InnoDB中的索引是通过段,簇,页面构成的。
段
段是一个逻辑概念,是构成索引,表,回滚段的基本元素。一个索引包含两个段,分别是内节点段和叶子段,内节点段用来管理B+数中非叶子节点的数据,叶子段用来管理B+书中叶子节点的数据。
簇
簇是构成段的基本元素,一个段至少会有一个簇,簇的大小空间是固定的,一般为64个页面。每个段和每个簇的物理位置是没有关系的。
页面
一个页面默认为16KB
段,簇,页面组织结构
一个表空间可以有多个ibd文件,至少有一个文件,被称为“0号文件”每个文件被划分为相同大小的块,就是页,0文件的第一个页面中存储了这个表空间中所有段簇页管理的入口,那么在这个页面中,可存储的数据就是16KB,但通常都会有页面同信息会占用一些空间。真正的管理信息数据是从页面偏移为FIL_PAGE_DATA(38)的位置开始的。这个位置存储了表空间描述信息,描述信息包括如下内容
- FSP_SPACE_ID 表空间ID号
- FSP_SIZE 当前表空间总的页面数
- FSP_FREE 这个链表,相当于是一个表空间的簇池子,不用了的簇就可以放到这里
- FSP_FREE_FRAG 如果这个簇中已经有被使用的页面,这个簇就被称为半满簇,这个地址就是存储所有半满簇的链表头
- FSP_FULL_FRAG 这个地址是用来存储所有满簇的链表头指针
- FSP_FRAG_N_USED 这个值表示所有被使用过的页面的数量
- FSP_SEG_ID 在表空间中,每一个段都有一个唯一分配的ID号,指向下一个段的ID号
- FSP_SEG_INODES_FULL Inode代表一个数据段,是一个结构体,这个地址代表满Inode页面的链表的头指针
- FSP_SEG_INODES_FREE 不满或者为空的Inode页面的头指针
创建一个段的过程
根据表空间ID号得到表空间头信息。
从得到的表空间头中分配一个Inode,首先判断FSP_SEGJNODES_FREE链表中是否还 有空闲的Inode页面,如果有,则从页面的数据存储位置开始扫描,每一个Inode的大 小是固定的,所以扫描的步长也是固定的。每找到一个Inode后判断Inode描述符中 的FSEGJD是否为0,如果是,则没有使用,否则便是已经使用过了,找到第-个为0 的则返回,说明已经找到了合适的Inode,如果找到后发现这个Inode是这一页的最后 一个Inode,则将这个页面从FSP_SEG_INODES_FREE链表中摘下来,同时将这个页面 插入到FSP_SEGJNODES_FULL链表中。如果FSP_SEGJNODES_FREE链表中没有空 闲的Inode页面,则需要重新分配一个Inode页面,分配后将所有的Inode描述符中的FSEG_ID置为0,表示未使用,然后将这个页面链接到FSP_SEG_INODES_FREE链表中, 然后直接从这个页面中分配一个空闲的Inode,过程如上所述。
-给新分配的Inode指定SEGJD号,这个ID号要从表空间头的FSP_SEG_ID中取出来作 为新段的1D号,然后将这个ID号写入到Inode的FSEG_ID中,同时更新FSP_SEG_ID 中的值,更新为ID+1,表示下一个段的ID号。
初始化这个Inode信息,将偏移FSEGNOT_FULL_N_USED处的值置为0。初始化链表FSEG_FREE、FSEG_NOT_FULL 和FSEG_FULLO
-从这个段中分配出一个页面,分配页面时首先找到表空间头上的半满簇链表FSP_FREE
FRAG,然后从链表中找一个簇描述符,找到簇描述符之后,从它的XDES.BITMAP中找一个状态为XDESFREE_BIT的页,_返回一个0〜63的下标index,再根据簇描述符计算
得到这个簇管理的64个页面的首页号page,之后申请到的真正页面号就是page+index。计算方法是首先得到这个描述页的页面号descr_page,然后再得到这个簇描述符在簇描 述页面中的序号seq_no,那么簇描述的首页为page=descr_page + seq_no * 640
-分配好页面之后,通过系统缓存得到页面号为page+index的页面,这个页面就是这个 段的首页面。在一个段的首页上,需要记录这个段对应的Inode的位置,Inode的位 置存储在页面头中,分别是FSEG_HDR_OFFSET (INODE在INODE页面中的偏移)、FSEG_HDR_PAGE_NO (INODE 所在的INODE 页面号)、FSEG_HDR_SPACE (INODE 所在的表空间号)。
到此为止,一个段就分配完成了。以后如果需要在这个段中分配空间,只要找到其首页, 然后找到对应的INODE即可分配空间。
InnoDb如何创建一个B+树
首先,创建一个段出来,这个段的地址要被存储到PAGE_HEADER+PAGE_BTR_SEG_TOP位置中,并且传入的page参数为0,说明存储到的页面就是这个段的首页
再创建首页之后,再在这个页面的PAGE_HEADER+PAGE_BTR_SEG_LEAF位置创建一个段,很明显,这个段就是一个B+树中的叶子段,创建外后,B+树对应的两个段就被成功创建了。
将上面所创建的段中的受页面初始化,将其设置为一个索引页面的格式。这个根页面,在数据字典中的索引表中存储的根页面号。
设置这个根叶中的索引ID,也就是这个B+树属于哪一个索引,初始化页面链表信息,目前只有一个页面,返回根页面号,在后面会将这个页面号写入刀SYS_INDEXES表中属于这个索引那行记录的最后一列中,也就是PAGE_NO列,这样就可以通过一个索引找到它的根页面信息了。
