🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
innodb在实现表空间(table space)基于文件IO之上构建的一层逻辑存储空间管理,table space采用逻辑分层的结构:space、segment inode、extent和page.在实现层的逻辑使用了磁盘链表这种结构来管理逻辑关系。我们先来介绍磁盘链表。 ## 1.磁盘链表 磁盘链表的实现fut0lst.*文件当中, innodb为了管理表空间和索引模块,定义了一个基于磁盘的链表,主要是用来保存磁盘数据结构之间的关系。这个链表不是基于内存指针的,而是基于page no和boffset来做位置绑定的。在innodb中定义了一个fil_addr_t的结构来做描述: ~~~ typedef struct fil_addr_struct { ulint page; /*page在space中的编号*/ ulint boffset; /*page中的字节偏移量,在内存中使用2字节表示*/ }fil_addr_t; ~~~ fil_addr_t可以通过fut_get_ptr函数来获得对应node的内存位置(flst_node_t) flst_node_t可以通过buf_ptr_get_fsp_addr来确定fil_addr_t。 flst_node_t中存有12个字节的内容,前6个字节(page:4 boffset:2)表示相对自己前一个node的fil_addr_t信息,后6个字节表示相对自己后1个node的fil_addr_t。除了flst_node_t以外,磁盘链表还有一个头信息flst_base_node_t,头信息是一个节点个数FLST_LEN(4字节) + FLST_FIRST (6字节)+ FLST_LAST(6字节). ### 1.1磁盘链表的结构关系 ![](https://box.kancloud.cn/2016-08-17_57b42160e9355.jpg) ## 2.space结构分析 在innodb的表空间中,所有的数据都是以page为单位来存储的,在space(表空间)中有两种 page: FSP_HDR/XDES Page、fseg inodes Page。每个page是以默认16KB的大小存储的, innodb在分配page的时候总以一个extent为单位一次性分配64个page。 ### 2.1 FSP HDR/XDES Page ### 2.1.1XDES结构分析(extent) 这个类型的page主要存储两类信息,前面112个字节存储的是File Space header信息,后面剩余的空间存储多个extent描述信息(XDES ),具体存储结构图如下: ![](https://box.kancloud.cn/2016-08-17_57b421610f94e.jpg) 只有space的第一个page会保存FSP header,其他的页是用0填充的。 每个XDES Page最大包含256个XDES descritptors Entry,每个XDES descritptors Entry对应的是一个extent。XDES descritptors Entry的结构描述如下: ![](https://box.kancloud.cn/2016-08-17_57b421612acbf.jpg) File Segment ID                 是当前extent所属segment的ID   XDES list                         是磁盘双向链表的一个节点,分别指向前一个XDES entry的page位置和后一个                                             XDES entry的page位置   state                                 extent的状态, XDES_FREE、XDES_FREE_FRAG、XDES_FULL_FRAG、                                             XDES_FSEG,在为XDES_FSEG的时候,表示这个extent已经隶属于一个                                             Segment,extent在创建的时候会指定成XDES_FSEG状态。一个extent在刚                                             分配时的状态XDES_FREE.  bitmap                              当前extent的所有page的状态索引,一个page占用2 bit,第一个bit表示是否被使用                                            状态,第二个位表示是否并 清空状态,清空状态暂时好像没有用 到,都是TRUE。 ### 2.1.2 FSP Header ![](https://box.kancloud.cn/2016-08-17_57b4216141f06.jpg)           space id                    当前表空间的ID           size                    当前space最大可容纳的page数,文件扩大时才会改变这个值           limit                   当前space已经分配初始化的page数,包括空闲的和已经使用的           flag                    未起作用           frage used         FSP_FREE_FRAG列表中已经被使用的page数           free list              space中可用的extent对象列表,extent里面没有一个page被使用           frag free list      有可用碎叶page的extent列表,exntent里面有部分page被使用           frag full list       没有有可用page的extent列表,exntent里面全部page被使用           segment id        下一个可利用的segment id           full inode list     space当前完全占满的segment inode页列表           free inode list     space当前完全占满的segment inode页列表 ### 2.2 Fseg inode Page 这个页类型是存储fseg inode用的页,每个inode 占用192个字节,一个page存储有85个inode对象,结构如下: ![](https://box.kancloud.cn/2016-08-17_57b4216160243.jpg) 在FIL Header后面紧接了12个字节,这个12个字节其实就是full inode list或则free inode list中的列表所以,分别表示前后的fil_addr_t。每个inode信息占用192个字节,里面分别管理对应的extent和fragment page。inode 结构如下: ![](https://box.kancloud.cn/2016-08-17_57b421612acbf.jpg)   fseg id                                    segment ID           not full used                          FSEG_NOT_FULL列表中的page数           FSEG_FREE                         inode中空闲的extent列表           FSEG_NOT_FULL               extent有部分page被占用,有部分page空闲的extent列表           FSEG_FULL                          完全占满的extent的列表           FSEG_MAGIC_N                  校验魔法字           fragment array                       一个长度为32的零散page索引存储的数组,如果这个数据满了.主要的作用是                                                            节省空间,例如在表刚建立时,不会分配一个完整的extent给表用,只会分配                                                            6个PAGE页,这时候就需要用fragment array来管理。 ## 3.space结构图 ### 3.1space框架关系图 ![](https://box.kancloud.cn/2016-08-17_57b4216180636.jpg) ### 3.2模块关系示意图 ![](https://box.kancloud.cn/2016-08-17_57b42161a7c2d.jpg) ## 4.space的inode、extent和page分配流程 innodb的space中,inode、extent和page之间的关系是环环相扣的,inode对应的是segment,extent对应的是区,page是页,也是表空间的最小分配单位。一个page在MySQL中默认是16KB大小,一个extent管理64个page,大小为1M,而inode可以管理很多extent加32个frag page(碎页)。frag page是为了节省空间而定义的。在了解了以上基本的概念后,我们开始分析inode的分配、extent的分配和page的分配过程。 ### 4.1 inode的分配流程 通过inode page的介绍我们可以知道,inode信息一定是存储在inode page中的,在分配inode的时候,一定是从inode page中获取空闲的inode。如果没有inode page可以使用,会先去在space的free list得到一个inode page(在函数fsp_alloc_seg_inode_page),然后再在这个inode page获得空闲的inode。在这个过程中会涉及到两个磁盘链表:FSP_SEG_INODES_FREE和FSP_SEG_INODES_FULL,这两个队列是管理inode page的,如果没有空闲inode的inode page是放在FSP_SEG_INODES_FULL中的,如果还有空闲inode的inode page是放在FSP_SEG_INODES_FREE中。一个inode页包含85个inode信息。以下是inode 分配示意图: ![](https://box.kancloud.cn/2016-08-17_57b42161c5dc6.jpg)   第1步:在FSP_SEG_INODES_FREE为空时,向space默认的头页中获取一个inode page,对应函数fsp_alloc_seg_inode_page   第2步:在申请inode时,如果FSP_SEG_INODES_FREE有可以的inode page,从inode page或的一个inode,对应函数fsp_alloc_seg_inode   第3步:如果在申请inode后,inode所处的inode page已经没有空闲的inode了,会将这个inode page放入FSP_SEG_INODE_FULL,并将其从FSP_SEG_INODES_FREE中删除。   第4步:如果inode管理的所有的页都是空闲,那么这个inode状态会被置为空闲状态,这个时候会将这个inode page从FSP_SEG_INODE_FULL移 到FSP_SEG_INODES_FREE中;这个过程只有在segment删除的时候才会调用。对应的函数fsp_free_seg_inode ### 4.2extent的分配流程 extent的分配方式有两种,一种是通过inode进行申请分配,一种是通过fragment碎片方式申请分配。inode分配方式是当inode中没有空闲可用的extent的时候,会向space free list中申请1个或者5个extent进行管理,如果当inode管理的extent数量小于40时,每次只会申请1个extent,如果超过这个大小,就会一次申请5个extent,这个过程会涉及到inode的FSEG_FREE、FSEG_NOT_FULL和FSEG_FULL三个磁盘链表。第二种申请方式是分配frag page时,是直接对extent进行申请,这其中会涉及到FSP_FREE_FRAG和FSP_FULL_FRAG这两个磁盘链表。以下是分配示意图: ![](https://box.kancloud.cn/2016-08-17_57b42161e5222.jpg) 上图中,1~7是属于inode申请分配流程, 8~12是属于frag page的申请extent方式   1: 当inode的free list为空,如果需要使用申请使用新的extent,innodb会从space free list获得空闲的extent加入到inode free list当中。   2: 当inode  free list中有extent,如果申请使用新的extent,只只需要从inode free list中拿取,并将extent移到inode not full当中。   3:只是通过inode方式申请页的一个操作,这个时候extent有足够多的空闲page.   4: 当extent中没有空闲的page时,会将这个extent从inode not full中转移到inode full当中。   5: 当一个page释放时,这个page所处的extent是一个完全占用的且被inode管理的extent,那么page释放后,就会将这个extent从inode full移到inode not full   6:当一个page释放时,这个page所处的extent有且只有这一个page被占用,那么page释放后,这个extent就会归还给inode list.并且会直接进行 7将extent归还给space free list. ** 8~12和以上步骤类似** ### 4.3page的分配流程 page的申请分配是基于inode 申请和extent申请的基础上,页的申请有外部通过inode方式申请,也有通过fragment page方式申请。fragment方式申请相对比较简单,就不在表述,源码中很清晰。inode方式分配是比较复杂的,其主要实现是在fseg_alloc_free_page_low和fseg_free_page_low这两个函数。在fseg_alloc_free_page_low函数中实现了7种情况获得inode中的page.   1. 指定的inode的hint位置的页是空闲状态,直接返回对应的page   2.descr是空闲状态,但segment inode中的空闲page数量 < 1/8,且碎片页被全部用完,为其分配一个extent,并获得hint对应的page   3.如果descr不是空闲状态,且segment inode中的空闲page数量 < 1/8,在inode当中获得一个空闲的extent,并且将这个extent descr对应的页返回。   4.descr是XDES_FSEG状态,且这个extent中还有空闲page,从其中获取一个page.   5.除了以上情况外,如果descr不是空闲的,但是inode还有其他的空闲extent,从其他的extent获得一个空闲。   6.如果其他的extent没有空闲页,但是fragment array还有空闲的碎片page,从空闲的碎片page中获得一个空闲页。   7.如果连碎页也没有,直接申请分配一个新的extent,并在其中获取一个空闲的page. ## 5.综述 table space的实现在fsp0fsp.*文件当中,也依赖于page0page.*  fil0fil.* 等文件。innodb在存储上,定义了最小的存储单位就是page,space在设计这些层关系,都是为了更为高效和合理的管理page。space可以和其他表存在同一个数据库文件中,也可以一张表一个文件存储。这取决于MySQL的配置。分析space的结构和工作原理有利于我们理解innodb的存储方式,其后面理解索引、锁和事务提供有力的基础。上面也说到最小的存储单位是page,我将在下一章节中单独来介绍数据page的存储方式和其工作原理。