(搬)Innodb中数据的物理结构

125 阅读5分钟

前言

本文仅阐述有关 Barracuda 文件格式的内容,即 Compact 行格式。(百恼注:本文 记录=原文的record 可以理解为一行数据,大家别跟我一样理解为动词而晕了)

记录偏移量

记录偏移量会指向记录数据自身,记录数据自身和头部都是变长的,本文内,以 N 标记数据的起始位置,N+x 即数据,N-x 即记录头部。在Innodb中,N这个位置被称作 origin

记录头

头的结构如下:

记录头结构.png

从下往上说,即从位置 N 往后数:

  1. 下个记录偏移量:从当前位置到下个记录的相对偏移量,该偏移的计算依据它们在页中按key顺序排列的顺序。

  2. 记录类型:记录的类型,目前仅支持4种:普通(0),节点指针(1),下确界(2),上确界(3)。

  3. 次序:该记录插入堆中的次序。堆中记录(包含上确界和下确界)是从 0 开始编号,下确界永远是 0,上确界永远是 1,用户记录从 2 开始。

  4. 记录的编号:记录在页目录里“拥有”的编号。这个会在未来讲页目录时详谈。

  5. 信息标记:一个 4bit 的位图,用于存储这个记录的 boolean 类型标记,目前只有两个标记被定义出来:

   1. min_rec(1) :B+ 树中,该记录属于非叶子节点的最小值

   2. deleted(2) :该记录被标记删除(未来会在 prune 行为中被真正清除)

  1. 可空字段位图(可能没有):用于存储字段是否可空,每个字段 1 位,全统计起来以一个字节数表示(百恼注:类似用位运算整合一系列 bool 值的用法)。如果一个字段是 null,它的值会从 key 或者行中剔除。如果没有可空字段,这个位图就不存在

  2. 可变长字段长度数组(可能没有):一个整型数组,整型可能为 8bit 或 16bit,取决于字段的最大长度,每个数字都代表一个可变长字段的数据长度。如果没有可变长字段,则该数组不存在。

记录头最小 5 字节,假如这个记录有一堆的大字段,那它也会大很多。

聚集索引

聚集键(主键、叶子页)有更复杂的记录结构:

聚集索引.png

(百恼注:N 以上的内容为记录头)

下面是记录数据里的内容:

  1. 聚集键字段:聚集键字段,如字面意思:连起来的键。InnoDB 只是把记录中每一列类型的原始字节连成单个字节流,这些原始字节代表了列类型的内部存储格式。

  2. 事务id:最新修改这个记录的事务id,这个id是 48bit 的整型。

  3. 回滚指针(百恼注:原文 roll pointer):记录最近更改事务回滚位置的结构体,该位置指向事务重做记录的回滚片段(百恼注:原文 rollback segment)。该结构体长这样:

   1. 1bit “是否插入”标记

   2. 7bit 回滚片段id

   3. 4字节的页码

   4. 2字节重做日志的偏移量

  1. 非键字段:所有非键字段连成一个字节流,即所有非主键字段的实际数据

非叶子页的记录跟它很像,但稍微简单点:

非叶子页索引键.png

自从非叶子不是 MVCC 后,事务 id 和回滚指针就被剔除。子页号码则代替了非键字段,并指向它。由于聚集索引键不能为空,可空字段位图不存在。

二级索引

(百恼注:这段描述不看图很绕,建议先看下几行的图)

先看叶子页。

InnoDB 的二级索引有和聚集索引几乎一模一样的结构,但不包含非键字段,取而代之的是聚集键字段,即主键的值(PKV)。

假如有字段同时存在于聚集索引和二级索引,那么就会被从聚集索引键字段中移除,并存入二级索引键字段里。举例:有个表主键是(a,b,c),它还有个二级索引键是(a,d),那么二级索引还是(a,d),但主键值只会保留(b,c)。

因二级键允许不唯一和可空字段,可变长字段数组和可空字段位图可能都不存在。这样一来叶子页的结构会很简单:

二级索引叶子页.png

二级索引字段也是将键值串连起来形成单个字节流,下方聚集索引字段同理。

二级索引的非叶子页大同小异:

二级索引非叶子页.png

非叶子页中有一点需要注意:聚集索引键字段(PKV)被包含在记录里并被视为键的一部分,而不是值的一部分。这是因为二级索引可能是不唯一的,但页中的每个记录又需要一个唯一标识,因而 PKV 必须被包含在记录里以满足唯一性。这意味着在非叶子页中,二级索引键的记录会比它对应的叶子页大上 4 字节。

行开销的一些题外话

看完上面的说明,你能轻松算出 InnoDB 每行的开销。聚集键叶子页需要给头部至少5字节,给事务 ID 6 字节,给回滚指针 7 字节,总计每行 18 字节。对于很窄的表,比如只有 2~3 个整型的表,这个开销比例是很高的。

另外,有大量行开销的情况下还不能有效填充页的话,会产生大量的空间浪费(实际情况下,可能页空间只填充了一半)。

接下来?

在下一篇文章里我会讲述页文件夹,以及它高效回收记录的意义。

原文地址

The physical structure of records in InnoDB