MySQL行格式

457 阅读5分钟
  • 行格式

    InnoDB会将数据划分成若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为16kb。而每个页中是按照行格式存储每条插入的数据。InnoDB中分别有四种行格式:Compact、Redundant、Dynamic、Compressed。其中数据库默认的行格式是 Dynamic (可以使用show table status like 'table_name'查询表的行格式)

    如果需要指定行格式可以使用

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

    或者在创建表的时候指定行格式

    CREATE TABLE table_name (
    c1 VARCHAR(10),
    c2 VARCHAR(10) NOT NULL,
    c3 CHAR(10),
    c4 VARCHAR(10)
    )
    CHARSET=ascii ROW_FORMAT=COMPACT;
    
    • Compact行格式

一条完整的记录在Compact行格式中表现为记录的额外信息和真实数据两部分。记录的额外信息又包括变长字段长度列表和null值列表和记录头信息。

为什么要保存变长字段长度呢?

在mysql中类似varchar()这样的类型存储数据的真实字节数是不确定的所以我们在存储真实数据的时候要顺便将其真正的字节数也存储起来。我们要注意的是这个列表的顺序是**逆序**的。什么叫逆序的呢?比如有三个varchar()字段在真实数据的存储顺序是t1,t2,t3。那么在变长字段列表中就是t3的长度,t2的长度,t1的长度。

类似的我们也需要将为null的字段标识出来,但是如果我们在真实记录中标记每个为null的字段那么耗费的空间将会大大增多。所以在记录的额外信息中设置了一个null值列表。**null值列表中使用二进制(1,表示该列为null0,表示不为null)倒叙表示每个列是否为null**。比如字段t1,t2,t3中t3为null且顺序排列,那么应该表示为011。而且mysql规定在null值列表中必须用整数个字节来表示,所以当位数不全的时候要不全,因此上面的例子的最终表示为00000011

记录头信息,它是由固定的5个字节组成。5个字节也就是40个二进制位,不同的位代表不同的意思.记录头信息比较复杂而且表示的东西比较多,但是大多数的东西我们并不会用到。这里摘抄大佬列举的详细信息:

| 名称         | 大小(单位bit) | 描述                                                         |
| ------------ | --------------- | ------------------------------------------------------------ |
| 预留位       | 1               | 没有使用                                                     |
| 预留位2      | 1               | 没有使用                                                     |
| delete_mask  | 1               | 标记该记录是否被删除                                         |
| min_rec_mask | 1               | B+树的每层非叶子节点中的最小记录都会添加该标记               |
| n_owned      | 4               | 表示当前记录拥有的记录数                                     |
| heap_no      | 13              | 表示当前记录在记录堆的位置信息                               |
| record_type  | 3               | 表示当前记录的类型,`0`表示普通记录,`1`表示B+树非叶子节点记录,`2`表示最小记录,`3`表示最大记录 |
| next_record  | 16              | 表示下一条记录的相对位置                                     |

除了记录的额外信息之外,MySQL在真实数据中也额外创建了三个字段DB_ROW_IDDB_TRX_IDDB_ROLL_PTR。其中DB_ROW_ID是	在没有自定义主键和Unique键的情况下才会添加此列用以作为唯一标识。 DB_TRX_ID是事务IDDB_ROLL_PTR是回滚指针。
  • Redundant行格式

    在Redundant行格式中记录的真实数据较Compact格式没有什么变化,主要变化在记录的额外信息中。Redundant的额外信息中只有两项,一项为字段长度偏移列表,一项是记录头信息。

    记录头信息和Compact相比较存在两点不一样的地方。1.Redundant行格式多了n_field1byte_offs_flag这两个属性。2.Redundant行格式没有record_type这个属性。

    字段长度偏移列表。这里要和Compact的变长字段长度列表区分开来。二两看似很像,但是却不是一个东西。

    字段长度偏移列表是指将每个字段的字段长度偏移量都记录下来,所以这里存储的是每个字段的长度偏移量的逆序。比如有字段t1,t2,t3。在字段长度偏移列表中存储的为25 24 1A 。那么这三个字段的长度分别为 0x06个字节,(0x0C - 0x06)个字节, (0x13 - 0x0C)个字节 即6个字节,6个字节,7个字节

    • CHAR(M)列的存储格式

      在Compact行格式中当列采用的是定长字符集时,该列占用的字节数不会被加到变长字段长度列表,而如果采用变长字符集时,该列占用的字节数也会被加到变长字段长度列表。而且对于CHAR(M)类型的列要求至少占用M个字节,而VARCHAR(M)却没有这个要求。

      然而在Redundant行格式中只要使用CHAR(M)类型,占用的真实数据空间就是该字符集表示一个字符最多需要的字节数和M的乘积,所以使用Redundant行格式的CHAR(M)类型的列是不会产生碎片的。

  • Dynamic和Compressed行格式

    目前InnoDB引擎默认的行格式是Dynamic,但是这两种行格式类似于COMPACT行格式,只不过在处理行溢出数据时有点儿分歧,它们不会在记录的真实数据处存储字符串的前768个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据处存储其他页面的地址。

    另外,Compressed行格式会采用压缩算法对页面进行压缩。

资料来源:[MySQL 是怎样运行的:从根儿上理解 MySQL]