第 5 章 盛放记录的大盒子——InnoDB数据页结构

5.1 不同类型的页简介

InnoDB为了不同的目的设计了许多不同类型的页。

5.2 数据页结构快览

在这里插入图片描述

名称中文名占用空间大小简单描述
File Header文件头部38字节页的一些通用信息
Page Header页面头部56字节数据而专有的一些信息
Infimum + Supremum最小最大记录26字节两个虚拟的行记录
User Records用户记录不确定实际存在的行记录内容
Free Space空闲空间不确定页中尚未使用的空间
Page Directory页面目录不确定页中某些记录的相对位置
File Trailer文件尾部8字节校验页是否完整

5.3 记录在页中的存储

在这里插入图片描述

初始阶段,没有User Record这个部分,每当我们插入一条记录,都会从Free Space部分,申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了。

5.3.1 记录头信息的秘密
CREATE TABLE page_demo ( 
	c1 INT, 
	c2 INT, 
	c3 VARCHAR ( 10000 ), 
	PRIMARY KEY ( c1 ) 
) CHARSET=ASCII ROW_FORMAT=COMPACT;

在这里插入图片描述

名称大小(bit)描述
预留位11没有使用
预留位21没有使用
delete_mask1标记该记录是否被删除
min_rec_mask1B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned4表示当前记录拥有的记录数
heap_no13表示当前记录在记录堆的位置信息
record_type3表示当前记录的类型
next_record16表示下一条记录的相对位置

在这里插入图片描述

插入数据

INSERT INTO page_demo VALUES
	(1, 100, 'aaaa'), 
	(2, 200, 'bbbb'), 
	(3, 300, 'cccc'),
	(4, 400, 'dddd');

在这里插入图片描述

  • delete_mask

    标记该记录是否被删除。0:没有被删除,1:已经被删除

    被删除的记录不会立即从磁盘上移除,因为移除它们之后把其他的记录在磁盘上重新排列需要性能消耗,所以只是打一个删除标记而已,所有被删除掉的记录都会组成一个垃圾链表,这个链表中的记录占用空间被称为可重用空间,之后如果有新记录插入到表中的话,可能把这些空间覆盖掉。

  • min_rec_mask

    B+树的每层非叶子节点中的最小记录都会添加该标记。0:不是B+树的非叶子节点中的最小记录

  • n_ownd

    表示当前记录拥有的记录数。

  • heap_no

    • 表示当前记录在本的位置。

    • 两条伪记录:最小记录和最大记录,heap_no分别为0和1,也就是说它们的位置最先前。

    • 通过主键来比较大小

    • 由于不是我们定义的记录,所以不存放在User Record,而是放在Infimum + Supremum部分

    • 示意图如下

在这里插入图片描述
在这里插入图片描述

  • record_type

    表示当前记录的类型。0:普通记录,1:B+树非叶节点记录,2:最小记录,3:最大记录

  • next_record

    • 表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量。
    • 单向链表,通过一条记录找到它的下一条记录
    • 按主键大小排序
    • 第一条是Infimum记录,最后一条是Supremum记录
    • 删除记录相当于删除链表中的一个节点

在这里插入图片描述
删除一条数据

DELETE FROM page_demo WHERE c1 = 2;

在这里插入图片描述
可以看出:

  1. 第2条记录还在,只是delete_mask改成了1
  2. 第2条记录的next_record变成了0,意味着没有下一条记录了
  3. 第1条记录的next_record指向了第3条
  4. n_owned从5变成了4

再插入一条数据

INSERT INTO page_demo VALUES(2, 200, 'bbbb');

在这里插入图片描述

InnoDB并没有因为新记录的插入而为它申请新的存储空间,而是直接复用了原来被删除记录的存储空间。

5.4 Page Directory(页目录)

  1. 将所有正常的记录(包括最大和最小记录,但不包括标记为已删除的)划分为几个组
  2. 每个组的最后一条记录的头信息中的n_owned属性表示该组内共有几条记录
  3. 将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到的尾部,这个地方就是所谓的 Page Directory。页面目录中的这些地址偏移量被称为为(Slot),所以这个页目录就是由组成的。

分组中的记录条数规定:最小记录所在的分组只能有1条记录,最大记录所在的分组能有1 ~ 8条记录,剩下的分组中记录条数的范围是4 ~ 8条之间。

再插入一些数据

INSERT INTO page_demo values
	(5, 500, 'eeee'), (6, 600, 'ffff'), (7, 700, 'gggg'),
	(8, 800, 'hhhh'), (9, 900, 'iiii'), (10, 1000, 'jjjj'), 
	(11, 1100, 'kkkk'), (12, 1200, 'llll'), (13, 1300, 'mmmm'),
	(14, 1400, 'nnnn'), (15, 1500, 'oooo'), (16, 1600, 'pppp');

在这里插入图片描述
所以在一个数据页中查找指定主键值的记录的过程分为两步

  1. 通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录
  2. 通过记录的next_record属性遍历该槽所在的组中的各个记录

5.5 Page Header(页面头部)

反映一个数据页中存储的记录的状态信息。

名称占用空间(byte)描述
PAGE_N_DIR_SLOT2在页目录中的槽数量
PAGE_HEAP_TOP2还未使用的空间最小地址,也就是说从此处开始之后就是Free Space
PAGE_N_HEAP2本页记录的数量(包括最小和最大记录以及标记为删除的记录)
PAGE_FREE2第一个已经标记为删除的记录地址(各个已删除的记录通过next_record也会组合一个单链表,可以被重新利用)
PAGE_GARBAGE2已删除记录占用的字节数
PAGE_LAST_INSERT2最后插入记录的位置
PAGE_DIRECTION2记录插入的方向
PAGE_N_DIRECTION2一个方向连接插入的记录数量
PAGE_N_RECS2该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录)
PAGE_MAX_TRX_ID2修改当前页的最大事务ID,该值仅在二级索引中定义
PAGE_LEVEL2当前页在B+树中所处的层级
PAGE_INDEX_ID8索引ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF10B+树叶子段的头部信息,仅在B+树的Root页定义
PAGE_BTR_SEG_TOP10B+树非叶子段的头部信息,仅在B+树的Root页定义
  • PAGE_DIRECTION

    假如新插入的一条记录的主键值比上一条记录大,我们说这条记录的插入方向是右边,反之则是左边。

  • PAGE_N_DIRECTION

    假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记录下来。一旦方向改变了,这个值就是清零。

5.6 File Header(文件头部)

File Header对各种类型的页通用,页会以File Header作为第一个组成部分。

名称占用空间(byte)描述
FIL_PAGE_SPACE_OR_CHKSUM4页的校验和(checksum值),通过某种算法计算一个较短的值来代表很长的字符串
FIL_PAGE_OFFSET4页号,唯一的,InnoDB通过页号可以定位一个
FIL_PAGE_PREV4上一个页的页号
FIL_PAGE_NEXT4下一个页的页号
FIL_PAGE_LSN8页面被最后修改时对应的日志序列位置
FIL_PAGE_TYPE2页的类型
FIL_PAGE_FILE_FLUSH_LSN8仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的 LSN 值
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4页属于的表空间
类型名称十六进制描述
FIL_PAGE_TYPE_ALLOCATED0x0000最新分配,还没使用
FIL_PAGE_UNDO_LOG0x0002Undo日志页
FIL_PAGE_INODE0x0003段信息节点
FIL_PAGE_IBUF_FREE_LIST0x0004Insert Buffer空闲列表
FIL_PAGE_IBUF_BITMAP0x0005Insert Buffer位图
FIL_PAGE_TYPE_SYS0x0006系统页
FIL_PAGE_TYPE_TRX_SYS0x0007事务系统数据
FIL_PAGE_TYPE_FSP_HDR0x0008表空间头部信息
FIL_PAGE_TYPE_XDES0x0009扩展描述页
FIL_PAGE_TYPE_BLOB0x000ABLOB页
FIL_PAGE_INDEX0x45BF索引页,也就是我们所说的数据页
  • FIL_PAGE_PREV和FIL_PAGE_NEXT

    上一个和下一个页号,以此建立一个双向链表。
    在这里插入图片描述

5.7 File Trailer(文件尾部)

8字节组成

  • 前4字节代表页的校验和,和File Header中的校验和相对应。同步内存数据到磁盘时,会先同步File Header的校验和,完全写完时,File Trailer的校验和也会被写到页的尾部。如果完全同步成功,两个校验和应该是一致的。反之则意味着同步过程出了问题。
  • 后4字节代表页面最后修改时对应的日志序列位置(LSN)

5.8 总结

  1. InnoDB为了不同的目的设计了不同类型的页。
  2. 一个数据页可以被大致分为7个部分
  3. 每个记录的头信息中都有一个next_record属性,形成一个单链表
  4. InnoDB会把页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个槽,存放在Page Directory中。
  5. 每个数据页的File Header部分都有上一个和下一个页的编号,形成一个双链表
  6. 为保证从内存中同步到磁盘的页的完整性,页的尾部会进行二次校验。
  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 确实,MySQL的varchar类型在InnoDB存储引擎中的存储结构比较复杂。在InnoDB中,每个记录都被存储为一个B+树节点,每个节点都有一个固定大小的,通常为16KB。 当一个varchar类型的列被插入到InnoDB表中时,它会被拆分成两个部分:一个是实际的数据,另一个是长度信息。长度信息会被存储在记录头中,而实际的数据会被存储在记录数据中。 在InnoDB中,如果一个varchar类型的列的长度小于等于768个字节,那么它会被存储在记录数据中。如果一个varchar类型的列的长度超过了768个字节,那么它会被存储在单独的中,并且在记录中只存储一个指向这个的指针。 此外,由于InnoDB使用了行级锁定,每个记录都需要存储一个事务ID,用于实现MVCC(多版本并发控制)。因此,在InnoDB中,每个记录头还需要存储一个6字节的事务ID和一个2字节的回滚指针。 综上所述,当使用varchar类型时,需要注意其实际数据的长度和存储引擎的存储结构,以便更好地设计表结构和查询语句。 ### 回答2: MySQL的varchar存储结构确实是相当深奥的。在InnoDB存储引擎中,varchar类型的数据存储在表的记录中,其存储结构会影响数据写入、存储空间占用和查询性能。 首先,varchar类型的数据记录中是以变长字符串的形式进行存储的。这意味着,varchar字段占用的存储空间与其实际存储的数据长度相关,而不是固定的。相比之下,固定长度的数据类型(如char)在存储时会占用固定的存储空间,无论实际数据的长度是多少。 其次,varchar类型的数据记录中的存储格式是由一个表示长度的字节和真实字符串数据构成的。这个长度字段用于指示存储的实际数据的长度,使得数据库可以根据需要动态地分配存储空间,从而节省了存储空间。 此外,在InnoDB存储引擎中,varchar字段的数据存储在内部的某个位置,而不是直接存储在上。这是由于InnoDB采用了B+树的数据结构来组织数据,为了节省存储空间和提高数据访问效率,varchar字段的数据会被存储在叶子节点中。这样一来,在查询时可以更快地遍历和定位数据,提高查询性能。 综上所述,MySQL的varchar存储结构的深度体现在其变长存储方式、长度字段和数据存储位置等方面。了解和理解这些存储结构对于正确使用varchar类型的字段、优化存储空间和提高查询性能都是非常重要的。 ### 回答3: MySQL的varchar存储结构InnoDB引擎中确实是一个很深入的话题。InnoDB引擎是MySQL的默认引擎,它采用了B+树索引来存储数据。在InnoDB记录存储结构中,varchar类型字段经过了一系列处理。 首先,InnoDB将每个记录分为固定长度部分和变长长度部分。varchar字段属于变长长度部分。对于varchar字段,MySQL会额外存储一个指针,指向数据存储区域。 其次,在实际存储varchar字段值时,InnoDB会使用两种方式。对于较短的varchar字段值,会直接将其存储在记录数据域中。这样做的好处是可以减少额外的存储开销。 而对于较长的varchar字段值,InnoDB会将其存储在一个称为“Overflow Page”的额外存储空间中。Overflow Page的指针存储在记录数据域中。Overflow Page与主记录有一个单独的物理连接。 另外,需要注意的是,在InnoDB中,varchar字段的长度是可变的,存储的最大长度由定义时的最大长度决定。这与char字段是不同的,char字段的长度是固定的。 总之,MySQL的varchar存储结构InnoDB引擎中是相对复杂的。它采用了不同的存储方式来处理不同长度的字段值,既保证了数据的存储效率,又满足了灵活性的要求。对于开发人员来说,了解varchar存储结构对于正确使用和优化数据库非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值