mysql 学习 - InnoDB的行

Posted Yanyu.Wu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql 学习 - InnoDB的行相关的知识,希望对你有一定的参考价值。

InnoDB行

我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。InnoDB存储引擎到现在为止设计了4种不同类型的行格式,分别是Compact、Redundant、Dynamic和Compressed行格式

compact(行格式)

技术图片

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


关于变长字段

变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、各种TEXT类型,各种BLOB类型,我们也可以把拥有这些数据类型的列称为变长字段,变长字段中存储多少字节的数据是不固定的,这种字段叫做变长字段.

对于变长字段, 除了存储真实值外, 还需要将真实值的长度使用变长字段长度列记录

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

变长字段长度列表中只存储值为 非NULL 的列内容占用的长度,值为 NULL 的列的长度是不储存的

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


关于NULL值列表

表中的某些列可能存储NULL值,如果把这些NULL值都放到记录的真实数据中存储会很占地方,所以Compact行格式把这些值为NULL的列统一管理起来,存储到NULL值列表中.

主键列、被NOT NULL修饰的列都是不可以存储NULL值的,所以在统计的时候不会把这些列算进去

如果表中没有允许存储 NULL 的列,则 NULL值列表 也不存在了

二进制位的值为1时,代表该列的值为NULL。
二进制位的值为0时,代表该列的值不为NULL。

mysql规定NULL值列表必须用整数个字节的位表示,如果使用的二进制位个数不是整数个字节,则在字节的高位补0. (一个字节=8位)
技术图片
以此类推,如果一个表中有9个允许为NULL,那这个记录的NULL值列表部分就需要2个字节来表示了。


关于记录头信息

它是由固定的5个字节组成。5个字节也就是40个二进制位,不同的位代表不同的意思


技术图片

技术图片


隐藏列

除了上述的信息外, innnodb还为每条记录添加了隐藏列, 分别是: row_id(行id), transaction_id(事务id), roll_pointer(回滚指针)

InnoDB表对主键的生成策略:优先使用用户自定义主键作为主键,如果用户没有定义主键,则选取一个Unique键作为主键,如果表中连Unique键都没有定义的话,则InnoDB会为表默认添加一个名为row_id的隐藏列作为主键。所以我们从上表中可以看出:InnoDB存储引擎会为每条记录都添加 transaction_id 和 roll_pointer 这两个列,但是 row_id 是可选的(在没有自定义主键以及Unique键的情况下才会添加该列)。这些隐藏列的值不用我们操心,InnoDB存储引擎会自己帮我们生成的。


char(M) 与 varchar(M)

这两个类型的列, 在界定是否为可变长度的列是, 主要取决于列使用的字符集是否是定长的, 比如 ascII字符集是定长为1字节(8位), 而utf8是不定长的, 一个字符要占用1-3字节. char(10) 类型的列要占用的字节为 10-30字节.

varchar(M)中M的最大值也要取决于列的字符集类型, 正常如果是 ascII字符集, 一个字符=1字节时, 最大长度也不能为 65535, 因为还需要给出标识是否为null等等信息的空间. 所以根据字符集的定长, 可以计算出M的最大值.

技术图片

记录中的数据太多产生的行溢出

有如下所示数据库脚本语句:

mysql> CREATE TABLE varchar_size_demo(
    ->       c VARCHAR(65532)
    -> ) CHARSET=ascii ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO varchar_size_demo(c) VALUES(REPEAT(‘a‘, 65532));
Query OK, 1 row affected (0.00 sec)

MySQL中磁盘和内存交互的基本单位是页,也就是说MySQL是以页为基本单位来管理存储空间的,我们的记录都会被分配到某个页中存储。而一个页的大小一般是16KB,也就是16384字节,而一个VARCHAR(M)类型的列就最多可以存储65532个字节,这样就可能造成一个页存放不了一条记录的尴尬情况。

在Compact和Redundant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址(当然这20个字节中还包括这些分散在其他页面中的数据的占用的字节数),从而可以找到剩余数据所在的页,如图所示:
技术图片


从图中可以看出来,对于Compact和Redundant行格式来说,如果某一列中的数据非常多的话,在本记录的真实数据处只会存储该列的前768个字节的数据和一个指向其他页的地址,然后把剩下的数据存放到其他页中,这个过程也叫做行溢出,存储超出768字节的那些页面也被称为溢出页, 不只是 VARCHAR(M) 类型的列,其他的 TEXT、BLOB 类型的列在存储数据非常多的时候也会发生行溢出, 所以你不用关注这个临界点是什么,只要知道如果我们一条记录的某个列中存储的数据占用的字节数非常多时,该列就可能成为溢出列.

Dynamic和Compressed行格式

为什么要介绍行溢出, 是因为要引出 Dynamic 和 Compress 行格式

它们不会在记录的真实数据处存储字段真实数据的前768个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据处存储其他页面的地址,就像这样:
技术图片


Compressed 行格式和 Dynamic 不同的一点是,Compressed 行格式会采用压缩算法对页面进行压缩,以节省空间.

MySQL5.7,它的默认行格式就是 Dynamic











以上是关于mysql 学习 - InnoDB的行的主要内容,如果未能解决你的问题,请参考以下文章

学习高性能mysql

减少InnoDB死锁发生的方法有什么?MySQL学习

MySQL InnoDB 表返回 20% 的行然后停止

mysql中InnoDB存储引擎的行锁和表锁

mysql之innodb引擎的行锁和表锁

mysql之innodb引擎的行锁和表锁