A-A+

InnoDB源码解析-存储管理层次

2020年09月27日 数据库 暂无评论 阅读 26 次

InnoDB的存储层次

总的数据粒度:Row - Page - Extent - Segment - Tablespace。

数据格式

Row

行内容主要就是各列的值,外加一些flag信息。

Page

Page(页面)是固定大小的物理存储块。Page有多种用途,既可以存放一组行记录,也可以存放存储管理元数据。

具体地,page类型相关的源码:

 

 

一个FIL_PAGE_INDEX页面包含7部分:

Fil Header

Fil Header总计占38字节,代码参见 include/fil0fil.h

Page Header

代码参见 include/page0page.h

Extent

代码见 include/fsp0fsp.h "EXTENT DESCRIPTOR"一节

引入Extent的主要目的是批量分配一段连续的page,提升分配效率和数据局部性。

Extent Descriptor是描述Extent属性的元数据信息,占用40字节,主要包括:

Extent Descriptor本身也要存放到页面上,这个页面叫XDES Page(类型为FIL_PAGE_TYPE_XDES),一个Extent Descriptor也称为Extent Entry。

XDES Page的格式:

Segment

Segment是Page和Extent的资源集合,Segment可以管理extent和一些零散的page(最多32个)。

InnoDB中的索引对应两个segment:一个管理叶子节点,一个管理非叶子节点。

代码中,一个segment用inode entry这个数据结构来描述,参见include/fsp0fsp.h中FILE SEGMENT INODE部分。

Inode entry信息包括:

Segment元数据存放到类型为FIL_PAGE_INODE的页面上,页面内数据组织大致如下:

FSEG_INODE_PAGE_NODE:该字段用于串inode page链表。

表空间 TableSpace

共享表空间和独立表空间

如果innodb_file_per_table配置为ON,则每个表都有自己的frm和ibd文件,也就是独立表空间。

共享表空间的优点:表空间自动管理,可以分成多个文件上存储。

共享表空间的缺点:不同表混合存储,删除操作可能导致大量空隙。

独立表空间的优点:存储方式清晰,故障恢复相对独立;跨库移动单表容易实现;空间回收更容易;删除操作更容易处理。

独立表空间的缺点:文件数量多,单表大小受限于操作系统单个文件大小。

FSP Header

源码参见 include/fsp0fsp.h 中 'SPACED HEADER' 部分。

表空间第一个Page是FSP Header,其类型是FIL_PAGE_TYPE_FSP_HDR。它是表空间的root page,创建表空间时初始化(fsp_header_init)。

数据组织大图

源码

Tablespace

 

创建filespace

用户新建表时,InnoDB会创建一个 "表名.ibd" 文件,并初始化filespace中的内容。

 

分配Segment

 

 

分配Extent

 

分配Page

page的分配分为两种途径: 1)从全局的fsp上分配一个page,直接从FSP_FREE_FRAG或者FSP_FREE链表中分配一个extent,然后从这个extent中分配一个page。具体使用的场景包括分配inode page和为segment中的frag array分配page。 2)从segment中分配一个page。这种分配方式包括1),当segment中的frag array被填满时,会首先将全局的fsp上的extent分配给segment,然后再从已经分配segment的extent中寻找满足的条件的并分配page。

其实分配方式2)是供外部使用(比如BTree)的,通过fseg_alloc_free_page_general(这个对外的接口调用。

从全局fsp上分配page

 

从segment上分配page

 

page分配策略

page的分配按照以下策略一次去尝试分配.

  • 情况1: 目标xdes(hint page所属的xdes)已经分配给当前segment,并且hint page处于空闲状态;
  • 情况2: 目标xdes是完全空闲状态,并且当前空间占用满足要求(已使用的page超过已经占用的page的7/8,并且已使用的page数大于segment的frag array size)。那么将目标xdes分配给当前segment(能进入这个分支暗含的条件目标xdes不属于当前segment),并将hint page分配出来。
  • 情况3: 当direction不是FSP_NO_DIR(FSP_UP或FSP_DOWN, 这个条件用来限制是因为BTree分裂需要申请page,暂时先这样理解),并且当前空间占用满足要求。那么从segment中分配一个free的extent(fseg_alloc_free_extent),并分配extent中的第一个或者最后一个page,具体根据direction确定。
  • 情况4: 目标xdes已经属于给当前segment,并且处于未满的状态。那么从目标xdes中分配一个空闲的page
  • 情况5: 当前segment中有空闲的page。那么寻找一个空闲的page分配出去,寻找的顺序是首先查找FSEG_NOT_FULL链表,再查找FSEG_FREE链表。
  • 情况6: 当前使用的page数小于segment的frag array size。那么从table space中分配一个碎片page,并放在segment中相应的frag slot。
  • 情况7: 兜底的情况,分配一个extent(fseg_alloc_free_extent)。如果extent是free状态,那么分配其第一个page,如果extent是free frag状态,那么分配第一个空闲的page。

单独的去理解每一种情况并不能完整的理解整个分配策略,我们需要关注的是通过以上分配策略遵循那些原则,要达到那些效果?

原则 (优先级从高到低): 1、优先填满segment的frag array。 2、尽量使用已经分配给segment的空间,并使空间使用趋近于已使用的空间超过已占用空间的7/8。 3、尽量分配hint page。

对照以上原则,我们对上述7种情况进行解读: 情况1暗含已经满足原则1(因为如果原则1没有满足,当前segment大概率不会有extent),并且也满足原则2(从已经占用的空间上分配page),当然很明显满足原则3。 情况2很明显是在瞒住原则1和原则2的前提下,去满足原则3。 情况3是在满足原则1和原则2,并且原则3已经确定无法满足的前提下,分配page ...剩余几种情况供读者去理解,应该是很好对应的

效果 1、避免小表占用太多空间。 2、使实际占用空间接近实际需要的空间。 3、使逻辑相邻的数据,在物理上也尽可能相邻。

标签:

给我留言