MySQL-3 行记录的存储结构

Posted efan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL-3 行记录的存储结构相关的知识,希望对你有一定的参考价值。

本文同样参考小孩子写的《mysql是怎样运行的:从根儿上理解MySQL》

来总结下,一条记录在底层的存储逻辑是怎样的。

InnoDB页简介

这个存储引擎是将表中的数据存储到磁盘上,真正的数据处理是在内存中,innodb将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,大小一般为16KB

行格式

指定行格式:

CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称
    
ALTER TABLE 表名 ROW_FORMAT=行格式名称

compact行格式

一条完整的记录可以被分为记录的额外信息记录的真实数据

一、记录的额外信息

是服务器为了描述这条记录而不得不额外添加的一些信息,分为:变长字段长度列表、null值列表和记录头信息。

变长字段长度列表

变长字段的数据类型如:varchar(M) 各种text类型、各种blob类型,这种变长字段中存储多少字节是不固定的,所以变长字段的存储空间分为2部分:真正的数据内容+占用的字节数。

  • 在compact行格式中,把所有变长字段的真实数据占用的字节长度都存放在记录的开头部位,从而形成一个变长字段长度列表,各变长字段数据占用的字节数按照列的顺序逆序存放

  • 这些字节选取的存储方式:如果该可变字段允许存储的最大字节数W*M超过255且真实存储的字节数超过127字节,则使用2个字节,否则使用1个字节。

  • 只存储值为非NULL的列内容占用的长度,值为NULL的列的长度是不储存的。

  • 并不是所有记录都有这个变长字段长度列表,比如说表中所有的列都不是变长的数据类型的,这一部分就不需要有。

NULL值列表

表中的某些列可能存储NULL值,如果都放到记录的真实数据中存储会很占地方,所以统一管理起来,存储到null值列表中,处理过程如下:

  • 1.首先统计表中允许存储null的列有哪些。

  • 2.如果表中没有允许存储null的列,则null值列表也不存在了。否则将每个允许存储null的列对应一个二进制位,二进制位按照列的顺序逆序排列,值为1时表示该列的值为null,值为0时,代表值不为null。

  • 3.必须用整数个字节的位表示,如果不是整数个字节,则在字节的高位补0。类推,如果有9个允许为null,则需要用2个字节来表示。

记录头信息

由固定的5个字节组成,即40个二进制位,不同的位代表不同的意思,如表示该记录是否被删除,拥有的记录数等。

  • delete_mask 标记该记录是否被删除
  • min_rec_mask,B+树的每层非叶子节点中的最小记录都会添加该标记
  • n_owned,占4位,当前记录拥有的记录数
  • heap_no,占13位,当前记录在记录堆的位置信息
  • record_type,占3位,当前记录的类型,0普通记录,1B+树非叶子节点,2最小记录,3最大记录
  • next_record,占16位,下一条记录的相对位置

delete_mask
用来标记该记录是否被删除,因为移除记录会把其他记录在磁盘上重新排列,所以只是打上删除标记,这些删除的记录会组成所谓的垃圾链表,在这个链表中记录占用的空间称之为可重用空间,之后如果有新记录插入到表中的话,可能把这些被删除的记录占用的存储空间覆盖掉

heap_no
表示当前记录在本页中的位置。
innodb自动给每个里加两个记录,称为伪记录或者虚拟记录,一个代表最小记录,一个代表最大记录,(对于一条完整的记录来说,比较记录大小就是比较主键的大小)

record_type

next_record
从当前记录的真实数据导下一条记录的真实数据的地址偏移量。下一条记录是指按照主键值有小到大顺序的下一条记录,

规定Infimum即最小记录的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录是Supremum即最大记录。指针指向下一条记录的 记录头信息和真实数据之间的位置,这个位置向左读取是记录头信息,向右读取是真实数据,这样可能会提高高速缓存的命中率(字段和字段长度信息在内存中的距离更近)

innodb对于已经删除的记录,再次插入时还是会重用这部分存储空间。

二、记录的真实数据

MySQL会为每个记录默认的添加一些列(隐藏列)。

innodb表对主键的生成策略:优先使用用户自定义主键,如果没有选取一个Unique键作为主键,否则innodb会为表默认添加一个名为row_id的隐藏列作为主键。

char(M)列的存储格式
对于char类型的列来说,当列采用的是定长字符集时,该列占用的字节数不会被加到变长字段长度列表,如果采用变长字符集时,该列占用的字节数也会被加到变长字段长度列表

行溢出

对于varchar类型的列最多可以占用65535个字节,减去记录的额外信息,是65532个字节,针对不同的字符集,分别存储的字符则是65532/3 。

在compact和Reduntant行格式中,对于占用存储空间非常大的列,在记录的真实数据只会存储该列的前768个字节,把剩余的数据分散存储在几个其他的页中,然后用20个字节存储指向这些页的地址,从而找到剩余数据所在的页,这些剩余的数据也叫做行溢出,存储这些页面的叫溢出页.

Dynamic和Compressed行格式

和compact行格式的区别是把所有的字节都存储到其他页面中,只在记录真实数据处存储其他页面的地址。

以上是关于MySQL-3 行记录的存储结构的主要内容,如果未能解决你的问题,请参考以下文章

MySQL高级--05--InnoDB数据存储结构---行格式(或记录格式)

C程序存储结构

提效小技巧——记录那些不常用的代码片段

用于从 cloudkit 检索单列的代码模式/片段

如何管理在每个 git 版本中添加私有代码片段?

MySQL的varchar水真的太深了——InnoDB记录存储结构