mysql innodb逻辑_Mysql——InnoDB逻辑存储结构

c2f36262fd9687a24001438d5fcf4913.png

简介

678be30657b6dd23274333dbf3e102ef.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_CHKSUM4代表该页的checksum值

FIL_PAGE_OFFSET4表空间页的偏移值,也就是该页在某个表空间所有页的的位置

FIL_PAGE_PREV4当前页的上一个页的位置

FIL_PAGE_NEXT4当前页的下一个页的位置

FIL_PAGE_LSN8该页最后一次被修改的日志序列位置LSN

FIL_PAGE_TYPE2页的类型

FIL_PAGE_FILE_FLUSH_LSN8“文件已经被刷新到磁盘上,至少到这个lsn”,仅在文件的第一页有效

FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4页属于哪个表空间

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

名称16进制说明FIL_PAGE_TYPE_ALLOCATED0x000该页为最新分配

FIL_PAGE_UNDO_LOG0x002Undo log页

FIL_PAGE_INODE0x003索引节点

FIL_PAGE_IBUF_FREE_LIST0x004插入缓冲空闲列表

FIL_PAGE_IBUF_BITMAP0x005插入缓冲位图

FIL_PAGE_TYPE_SYS0x006系统页

FIL_PAGE_TYPE_TRX_SYS0x007事务系统数据

FIL_PAGE_TYPE_FSP_HDR0x008File Space Header

FIL_PAGE_TYPE_XDES0x009扩展描述页

FIL_PAGE_TYPE_BLOB0x00ABLOB页

FIL_PAGE_INDEX0x45BFB+树叶节点

Page Header(页头):56字节,用来记录数据页的状态信息,由14个部分组成。

名称空间(字节)说明PAGE_N_DIR_SLOTS2表示页目录的插槽数

PAGE_HEAP_TOP2堆中第一个记录的指针,记录在页中是根据堆的形式存放的

PAGE_N_HEAP2堆中的记录数,第15位表示行记录格式

PAGE_FREE2指向可重用空间的首指针

PAGE_GARBAGE2已删除记录的字节数

PAGE_LAST_INSERT2最后插入记录的位置

PAGE_DIRECTION2最后插入的方向:

PAGE_LEFT(0x01)

PAGE_RIGHT(0x02)

PAGE_SMAE_REC(0x03)

PAGE_SMAE_PAGE(0x04)

PAGE_NO_DIRECTION(0x05)

PAGE_N_DIRECTION2一个方向连续插入的记录数

PAGE_N_RECS2该页的记录数

PAGE_MAX_TRX_ID8修改当前页最大的事务ID,注意该值仅在二级索引定义

PAGE_LEVEL2当前页在索引树的层数

PAGE_INDEX_ID8索引ID,代表该页属于哪个索引

PAGE_BTR_SEG_LEAF10B+树数据页非叶子节点所在段的segment header

PAGE_BTR_SEG_TOP10B+树数据页所在段的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继续查找相对应的记录。

0b12eaa31cf0209508e59c4b637bb5c1.png

File Trailer(文件结尾信息):8字节,为了检测页是否已经完整写入磁盘(如可能发生写入过程中磁盘损坏,机器关机等)。

名称空间(字节)说明FILE_PAGE_END_LSN8前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;查看表中的行记录格式。

3700d0257be39e6c3013c00ec2aeccad.png

Compact行记录格式

2e8201d2032a3a8b90c1d56d2b51a6a0.png

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

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

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

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

名称空间(bit)说明()1未知

()1未知

delete_flag1该行是否被删除

min_rec_flag1如果等于1,该记录是预先被定义为最小的记录

n_owned4该记录拥有的记录数

heap_no13索引堆中该条记录的排序记录

record_type3记录类型:

000表示普通

001表示B+树节点指针

010表示Infimum

011表示Supremun

1XX表示保留

next_record16页中下一条记录的相对位置

实际存储每个列的数据;

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

每行数据除了用户定义的以外,还有两个隐藏列:事务ID列(6个字节)和回滚指针列(7个字节)。

如果表没有定义主键,每行还会增加一个rowid列。

Redundant行记录格式

c023eeefab233b236c1e0773c3a7b1cc.png

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_flag1该行是否被删除

min_rec_flag1如果等于1,该记录是预先被定义为最小的记录

n_owned4该记录拥有的记录数

heap_no13索引堆中该条记录的排序记录

n_fileds10记录中列的数量

1byte_offs_flag1偏移列表为1个字节还是2个字节

next_record16页中下一条记录的相对位置

实际存储每个列的数据;

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

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

e418d0a5e1f20231f2561f30e3b81dd7.png

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个字节的指针,实际的数据都存放到外部溢出页中。

be8ded0d8849d368e709f27b8a689d8f.png

Compressed行记录格式

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值