InnoDB逻辑存储结构

简介

image.png

从InnoDB逻辑存储结构来看,InnoDB所有数据都存放到在一个空间中,称之为表空间。如图所示,表空间由段、区、页组成。

表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最外层。之前的文章Mysql——InnoDB存储引擎架构就已经介绍过了,表空间分为系统表空间、独立表空间、常规表空间、undo独立表空间、共享临时表空间。

表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。

  • 数据段:B+树的叶子节点;
  • 索引段:B+树的中间节点;
  • 回滚段:记录undo log。

段是由各个区组成的,而区是由多个页组成的,在任何情况下每个区的大小都为1M。为了保证区中页的连续性,InnoDB存储引擎申请空间是按区为单位申请(一次从磁盘申请4~5个区)。默认的情况下,InnoDB中页的大小=16KB,即一个区有64个连续页。

当然Mysql 5.7中InnoDB存储引擎时支持页的压缩,每个页的大小可能是2K、4K、8K、16K,所以每个区对应页的数量变成64~512之间。

考虑这样的一个问题,在用户启用了参数innodb_file_per_table后,创建的表默认大小为96K,上面说到每个区的是连续的64个页,创建表的时候应该是1M才对?

其实是每个段开始的时候,会先用32个页大小的碎片页来存放数据(也就是说创建表的时候默认会用到6个碎片页),当着32个碎片页用完的时候才会申请连续的64个页。对于一些小表来说,可以再开始的时候申请较少的空间,节省磁盘空间。

页是InnoDB磁盘管理的最小单位,默认的情况下,InnoDB中页的大小=16KB,数据库一开始可以通过innodb_page_size将页的大小设置为4K、8K、16K,但是一旦数据库表被创建了,后面就不能再修改了。
常见的页类型有:

  • 索引、数据页(B+Tree Node);
  • undo页(undo log Page);
  • 系统页(System Page);
  • 事务数据页(Transaction system Page);
  • 插入缓冲位图页(Insert Buffer Bitmap);
  • 插入缓冲空闲列表页(Insert Buffer Free List);
  • 未压缩的二进制大对象页(Uncompressed BLOB Page);
  • 压缩的二进制大对象页(Compressed BLOB Page)。

索引、数据页结构

即InnoDB存储引擎B+树的节点。InnoDB的数据页由以下7个部分组成:

  • File Header(文件头):38字节,用来记录页的头部信息,由8个部分组成。
名称 空间(字节) 说明
FIL_PAGE_SPACE_OR_CHKSUM 4 代表该页的checksum值
FIL_PAGE_OFFSET 4 表空间页的偏移值,也就是该页在某个表空间所有页的的位置
FIL_PAGE_PREV 4 当前页的上一个页的位置
FIL_PAGE_NEXT 4 当前页的下一个页的位置
FIL_PAGE_LSN 8 该页最后一次被修改的日志序列位置LSN
FIL_PAGE_TYPE 2 页的类型
FIL_PAGE_FILE_FLUSH_LSN 8 “文件已经被刷新到磁盘上,至少到这个lsn”,仅在文件的第一页有效
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 页属于哪个表空间

页的类型主要有以下几种:

名称 16进制 说明
FIL_PAGE_TYPE_ALLOCATED 0x000 该页为最新分配
FIL_PAGE_UNDO_LOG 0x002 Undo log页
FIL_PAGE_INODE 0x003 索引节点
FIL_PAGE_IBUF_FREE_LIST 0x004 插入缓冲空闲列表
FIL_PAGE_IBUF_BITMAP 0x005 插入缓冲位图
FIL_PAGE_TYPE_SYS 0x006 系统页
FIL_PAGE_TYPE_TRX_SYS 0x007 事务系统数据
FIL_PAGE_TYPE_FSP_HDR 0x008 File Space Header
FIL_PAGE_TYPE_XDES 0x009 扩展描述页
FIL_PAGE_TYPE_BLOB 0x00A BLOB页
FIL_PAGE_INDEX 0x45BF B+树叶节点
  • Page Header(页头):56字节,用来记录数据页的状态信息,由14个部分组成。
名称 空间(字节) 说明
PAGE_N_DIR_SLOTS 2 表示页目录的插槽数
PAGE_HEAP_TOP 2 堆中第一个记录的指针,记录在页中是根据堆的形式存放的
PAGE_N_HEAP 2 堆中的记录数,第15位表示行记录格式
PAGE_FREE 2 指向可重用空间的首指针
PAGE_GARBAGE 2 已删除记录的字节数
PAGE_LAST_INSERT 2 最后插入记录的位置
PAGE_DIRECTION 2 最后插入的方向:
PAGE_LEFT(0x01)
PAGE_RIGHT(0x02)
PAGE_SMAE_REC(0x03)
PAGE_SMAE_PAGE(0x04)
PAGE_NO_DIRECTION(0x05)
PAGE_N_DIRECTION 2 一个方向连续插入的记录数
PAGE_N_RECS 2 该页的记录数
PAGE_MAX_TRX_ID 8 修改当前页最大的事务ID,注意该值仅在二级索引定义
PAGE_LEVEL 2 当前页在索引树的层数
PAGE_INDEX_ID 8 索引ID,代表该页属于哪个索引
PAGE_BTR_SEG_LEAF 10 B+树数据页非叶子节点所在段的segment header
PAGE_BTR_SEG_TOP 10 B+树数据页所在段的segment header
  • Infimun和Supremum Records:属于InnoDB存储引擎每个页中的两个虚拟行记录,用来限定记录的边界。Infimun:下界,表示比任何主键值还要小的值;Supremum:上界,表示比任何主键值还要大的值;这两个值在页创建的时候被建立,在任何情况下都不会被删除。
  • User Records(行记录):用户实际存储的行记录。
  • Free Space(空闲空间):页的空闲空间。
  • Page Directory(页目录):存放记录的相对位置的指针,这些指针可以称之为Slots(槽)。在InnoDB中并不是每个记录都拥有一个Slot,而是一个稀疏目录,也就是说一个Slot可能包含多个记录。在Slots中记录是按照索引键值顺序存放的,这样可以利用二叉查找树迅速查找到对应的Slot。用户查找数据的时候先通过B+树找到对应的页,然后将页加载到内存,然后通过Page Directory进行二叉查找,找到对应的Slot,然后通过该Slot指向的记录进行next_record继续查找相对应的记录。
image
  • File Trailer(文件结尾信息):8字节,为了检测页是否已经完整写入磁盘(如可能发生写入过程中磁盘损坏,机器关机等)。
名称 空间(字节) 说明
FILE_PAGE_END_LSN 8 前4字节表示页的checksum值;后4字节与File Header的FIL_PAGE_LSN相同。将这两个值与File Header的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN进行比较,确认是否一致,以此来保证页的完整性。

在InnoDB存储引擎中,表都是根据主键顺序组织存放的,即按照主键作为索引键值,因此InnoDB存储引擎中的表实际上就是颗B+Tree,树的节点存放的是索引、数据页,其中叶子节点存放的是数据,非叶子节点存放的是索引,每个叶子节点包含多行数据。
InnoDB存储引擎对每个页数据的存放是有硬性要求的,最多允许16KB/2 - 200 = 7992行数据,最少需要2行数据。

行记录格式

InnoDB存储引擎提供了Redundant、Compact(Mysql5.1 默认)、Dynamic(Mysql5.7 默认)、Compressed四种格式来存放行记录,可以使用show table status;查看表中的行记录格式。

image

Compact行记录格式

image

上图展示了Compact行记录格式,主要有以下几部分:

  • 变长字段列表:存储变长字段的长度,按照列的顺序逆序来存储;如果列的长度<255,用1个字节表示;否则使用2个字节表示(注意,VARCHAR最大长度为65535);

  • NULL标志位:表示改行数据是否有空值,有则用1表示,该部分占用1个字节;

  • 记录头信息:固定5个字节。存储格式如下:

    名称 空间(bit) 说明
    () 1 未知
    () 1 未知
    delete_flag 1 该行是否被删除
    min_rec_flag 1 如果等于1,该记录是预先被定义为最小的记录
    n_owned 4 该记录拥有的记录数
    heap_no 13 索引堆中该条记录的排序记录
    record_type 3 记录类型:
    000表示普通
    001表示B+树节点指针
    010表示Infimum
    011表示Supremun
    1XX表示保留
    next_record 16 页中下一条记录的相对位置
  • 实际存储每个列的数据;

需要注意的是如果某个列的值为NULL,则不占任何空间(除了占有NULL标志位,为1)。

每行数据除了用户定义的以外,还有两个隐藏列:事务ID列(6个字节)和回滚指针列(7个字节)。
如果表没有定义主键,每行还会增加一个rowid列。

Redundant行记录格式

image

Redundant是Mysql5.0版本之前的行记录格式。上图展示了Redundant行记录格式,主要有以下几部分:

  • 字段长度偏移列表:存储所有字段长度的偏移量,按照列的顺序逆序来存储(例如:23 20 16 14 13 0c 06字段长度偏移列表,06代表第一列的长度=6,0c-06=第二列的长度,13-0c=第三列的长度,以此类推);如果列的长度<255,用1个字节表示;否则使用2个字节表示(注意,VARCHAR最大长度为65535);

  • 记录头信息:占用6个字节,存储格式如下:

    名称 空间(bit) 说明
    () 1 未知
    () 1 未知
    delete_flag 1 该行是否被删除
    min_rec_flag 1 如果等于1,该记录是预先被定义为最小的记录
    n_owned 4 该记录拥有的记录数
    heap_no 13 索引堆中该条记录的排序记录
    n_fileds 10 记录中列的数量
    1byte_offs_flag 1 偏移列表为1个字节还是2个字节
    next_record 16 页中下一条记录的相对位置
  • 实际存储每个列的数据;

①考虑这样的一个问题,之前说到页的大小默认16K(16384个字节),并且一个数据页中至少要包含2行记录,然而Mysql Varchar最大存储字节65535个字节(当然还有其他可变长大字段,TEXT、BLOB) > 页的大小,两者之间是否会互相矛盾?

事实上会互相矛盾,但mysql引入行溢出的机制来解决这个问题。

image

Compact和Redundant行记录格式采用上图存储方式,数据页只保留前768个字节的前缀数据,之后是偏移量指针,指向行溢出页。
②什么时候会触发行溢出呢?
刚刚讲到一个数据页至少要包含2行记录,因此,如果当前页只能存放一个记录的时候,InnoDB存储引擎就会自动把行数据存放到溢出页中。
③对于TEXT或者BLOB,是不是总是存放到溢出页中?
是否存放到溢出页中,都要遵循上面第②点。
④Varchar最大存储字节65535个字节,是指单个列的长度么?
不是的,是指一行记录的所有VARCHAR列长度的总和不能超过65535个字节。
⑤Varchar(n)或者Char(n)中的n指的是字节个数还是字符长度?
指的是字符长度。不同的字符集对列的长度会有影响,比如定义Varchar的最大长度,lantin则可以定义Varchar(65535),如果是utf-8(3个字节)则只能定义Varchar(65535/3)。对于char(n)来说,不同的字符集长度是不固定的,InnoDB存储引擎在内部会将其视为变长字符类型。

Dynamic行记录格式

InnoDB 1.0X版本开始引入新的文件格式,以前支持Compact和Redundant格式称为Antelope文件格式,新的格式有:Compressed和Dynamic,称为Baracuda文件格式。

Dynamic行格式提供与Compact行格式相同的存储特性,但对于长可变长度列值(对于VARCHAR,VARBINARY和BLOB和TEXT类型)行溢出采用了完全的行溢出方式。如图所示,在数据页的某行记录里的某个大可变长度列值只存放20个字节的指针,实际的数据都存放到外部溢出页中。
image

Compressed行记录格式

Compressed行格式提供与Dynamic行格式相同的存储特性和功能,但增加了对表和索引数据压缩的支持(采用zlib算法压缩)。

你可能感兴趣的:(InnoDB逻辑存储结构)