【MySQL进阶之路 | 高级篇】InnoDB搜索引擎行格式

1. COMPACT行格式

COMPACT行格式是MySQL5.1的默认行格式.其结构示意图如下.

f1d1fc7c0c0643c0803d555be1e43332.png

大体可以分为两部分.

  • 记录的额外信息.这里面有包括变长字段长度列表,NULL值列表和记录头信息.
  • 记录的真实数据.

(1).变长字段长度列表

MySQL支持一些变长的数据类型.比如VARCHAR(m), VARBINARY(n), TEXT类型.这些数据类型修饰的列称为变长字段.变长字段中存储多少字节的数据不是固定的,所以我们在存储数据的时候也顺便把这些数据占用的字节数也存储起来.在COMPACT行格式中,把所有变长字段的真实数据占用的字节数都存放在记录的开头部位,从而形成变长字段长度列表.

这里存储的变长字段长度和字段顺序是反过来的.比如此处应该存放为060408.

CREATE TABLE compact_demo(
c1 VARCHAR(8),
c2 VARCHAR(8),
c3 CHAR(8),
c4 VARCHAR(8)
) charset=ascii ROW_FORMAT=COMPACT;

INSERT INTO compact_demo
VALUES('zhangsan', 'lisi', 'wang5', 'ding1');

b23c63c65b0f4892947bd4d16e8b8b1a.png

(2). NULL值列表

compact格式会把可以为null值的列统一管理起来,存放在一个标记为null值的列表中.如果表中没有允许存储null值的列,则null值列表也就不存在了.

之所以要存储null值,是因为数据是需要对齐的.如果没有表明出来null值的位置,就有可能查询数据的时候发生混乱.如果使用一个特定的符号放到相应数据位表示null值的话,会很浪费空间,所以干脆直接在行数据开头开辟一处空间专门用来记录该行记录哪些是空数据,哪些是非空.

二进制位为1时,该列的值为null.为0时则该列的值为非空.

如果某字段明确了是非空的(比如NOT NULL/PRIMARY KEY),那么null值列表就不管这些字段了.

(3).记录头信息

记录头信息占有5个字节大小.用于描述记录的一些属性.

147c91ca8c9947a3a951edfc4e3fc39f.png

1. 删除标记位(Delete Flag):占1位,表示记录是否被标记为删除。在InnoDB中,记录被删除时并不是立即物理删除,而是通过设置这个标志位来标记记录为已删除,这样可以快速“删除”记录且不影响后续的插入操作。

2. 最小记录标记位(Min Record Marker):占1位,仅在页内最小记录上设置为1,表明这是页内用户记录中的最小记录。

3. 最大记录标记位(Max Record Marker):占1位,仅在页内最大记录上设置为1,表明这是页内用户记录中的最大记录。

4. heap_no:占4位,表示记录在页内的堆编号,用于标识记录在页中插入的顺序。

5. next_record:占16位或24位(取决于行格式),记录指向下一个记录的指针,用于遍历页内的记录链表。在Compact和Redundant行格式中通常是16位,在DYNAMIC和COMPRESSED行格式中可能使用24位以适应更大的页大小。

6. n_owned:占4位,在B+树的非叶节点中,表示子页的数量;在聚集索引的叶节点中,表示指向同一行的辅助索引记录数量。

7. record type:占3位,表示记录类型,常见的有:
   - 0:普通用户记录。
   - 1:目录项记录,用于B+树的内部节点。
   - 2:最小记录(Infimum)
   - 3: 最大记录(Supremum),这两个特殊的记录用来界定用户记录的边界,不存储实际用户数据。

1. delete_mask : 标记当前记录是否删除.其实,被删除的记录仍然在页中存储,还在真实的磁盘上.这些被删除的记录之所以不立即从磁盘上移除,因为移除他们之后其他记录在磁盘上需要重新排列,导致性能损耗.所以只是打个删除标记,而所有的被删除记录会组成一个垃圾链表.这个链表中的记录占用的空间称为可重用空间.之后如果有新记录插入到表中,可能会把这些删除的记录的存储空间的内容覆盖掉.

2. min_rec_mask/max_rec_mask

b+树的每层非叶子节点中最小记录都会添加该标记,min_rec_mask的值为1.

3. record_type : 这个属性表示当前记录的类型.0表示普通记录.1表示b+树非叶子节点记录.2表示最小记录.3表示最大记录.

4. heap_no : 这个属性表示当前记录在本页中的位置.MySQL会自动给每个页加了两个记录(Infimum和SuperMum),由于这两个记录不是我们自己添加的,所以也叫虚拟记录.这两个记录一个代表最小记录.一个代表最大记录.最小记录的heap_no为0,最大记录的heap_no为1.

5. n_owned : 页目录中每个组中最后一个记录的头信息会存储该组一共有多少条记录.

6. next_recode : 表示从当前记录真实数据到下一条记录的真实数据的地址偏移量.也就是指向下一条记录的指针.

(4). 记录的真实数据

记录的真实数据除了我们自己定义的列的数据外,还有三个隐藏列.

b51d6aeed5c54f9e81198bec49187a71.png

这些列的真实名称其实是 : DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR.

值得注意的是,除了第一个row_id,其他两个都是必须的.这是为什么呢?

原因我们讲过,创建聚簇索引时,我们优先选择用户自定义的主键作为主键,如果无,则会选择一个非空且唯一键作为主键,如果无,那么InnoDB会为表默认添加一个名为row_id的列作为隐藏列作为主键.

2. DYNAMIC/COMPRESSED行格式

MySQL5.7和8.0默认的行格式都是dynamic.

这两个行格式只有在行溢出这种情况下,有所区别,简单介绍一下行格式.

我们知道一个页的大小一般是16kb.也就是16384字节.而一个类型为varchar(m)的列最多可以存储65533个字节.这样就可能出现了一个页存放不下一条记录.这种情况叫行溢出.

  • 在COMPACT/REDUNTANT行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把其他剩余数据分散存储在几个其他的页中进行分页存储.然后记录的真实数据用20个字节存储指向这些页的地址.从而可以找到剩余数据所在的页.
  • 在DYNAMIC/COMPRESSED行格式中,对于存放在blog中的数据采用了完全行溢出的方式.比如在数据页中只存放20个字节的地址,实际数据都放在溢出页中.
  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值