InnoDB存储引擎的记录格式,数据页的结构

摘要:
InnoDB是一个存储引擎,根据我们的逻辑概念“表”负责访问磁盘数据。这里我们首先总结下表中的每一行数据由InnoDB存储,然后由磁盘存储的形式。InnoDB以多种形式存储行记录,例如紧凑、动态、冗余和压缩。record_Type表示记录的类型。0是正常记录,1是B+树的非叶节点,2是最小记录,3是最大记录。最大和最小记录由InnoDB自己插入。InnoDB页面的结构InnoDB页面有多种类型:索引页面、撤消日志信息页面、insertBuffer页面和inode节点信息页面。

  InnoDB是存储引擎,负责将磁盘的数据按照我们的逻辑概念“表”那样存取,这里首先总结下表中每一行数据是以何种形式被InnoDB存储再磁盘的。InnoDB的行记录存储形式有很多种,compact、dynamic、redundant、compressed等。下面介绍compact格式的行数据结构:

每一行的数据组成

  除了我们自己的真实数据外,为了提高性能和用于存取这行InnoDB需要加上必要的数据,这叫做额外信息。每一行数据都由额外信息 + 真实数据组成。 其中额外信息里又分为变长字段长度列表、null字段列表、记录头信息(记录头信息有很多用于存取行、管理行的数据);真实数据也并不是只有我们自己设置的数据,mysql还会为其添加一些字段例如:事务Id、回滚指针、行Id(没有设置主键时用于唯一标识行)。

额外信息的变长字段长度列表

  如果某个字段设置为变长,mysql必须知道这个字段实际长度,不然mysql取这个字段时根本不知道取多少。变长字段的长度就存放在额外信息的变长字段长度列表里,但是不是顺序的而是倒序的。当然,如果这一行没有变长字段,这一个列表就不存在。例如:表中有如下数据:

a(varchar(255))    |      b (varchar(256))       |      c(varchar(256))
---------------------------------------------------------------------
  sq        |     s.....(128个s)      |     s                    //第1行

第一行有三个变长字段,长度分别是 2B、128B、1B(我们假设字符集是ascii,一个字母一字节)。那么在变长字段长度列表里应该是:| 1  128  2  |,这几个数字占有内存的情况是:最大长度小于256,分配一字节;最大允许长度大于255且实际占用小于128,也分配一字节;最大允许长度大于255,且实际长度大于127则分配两字节。所以长度列表的内存分配应该是 1B + 2B +1B,共4B。

(由于记录变长字段实际大小的数字分配最躲2B空间,所以最多只能表示到65535,即每列最大65535B。)

额外信息的null值列表

  如果某几个列允许为null,那么为null值时不会记录在真实数据而是记录在额外信息里。和变长字段长度列表一样,也是倒序记录,采用bit记录。例如null值列表:0000 1001 ,表示第1个和第4个字段为null值。如果一行没有可以为null的字段,这个列表就不存在。

  值的注意的是:这个列表的内存单位是字节为单位。例如有7个可为null的字段,就需要分配1B记录;如果有9个字段可为null,就需要2B(因为需要9个bit了)。

额外信息的记录头信息

  这里有mysql操作记录所必须的数据,主要有:

  heap_no表示当前记录在记录堆的位置,根据主键从小到大排序。heap_no = x说明这条记录是第x条记录,InnoDB会自动插入一条最小记录(heap_no = 0)和一条最大记录(heap_no = 1)我们自己插的数据heap_no从2开始。

  next_record表示下一条记录的相对位置;这个非常重要,记录了从当前记录的真实数据开始地址到下一条记录真实数据首地址的偏移。例如当前记录真实数据起始地址x,next_record = y,那么下一条记录的真实数据首地址就为 x+y。总之,有了这个相对位置数据,一条条记录就像链表一样连接起来,删除某记录时直接修改前面记录的指针即可。当拿到真实数据的起始地址,向右可以读取每列的实际数据,向左可以读取变长字段长度列表,null值列表,这就是为什么这两个列表要逆序的原因。

  delete_mask表示记录是否删除。没错,实际存储删除记录并不是马上真的将磁盘的数据删掉,而是先标记起来,因为立即删除磁盘的重新排列会有较大的性能消耗。这些标记的行会组成一个垃圾链表,占用的空间叫做可重用空间,新纪录插入时就可能把这些空间的数据删除。如果我们再插入这条记录(主键一样),那么这条记录直接就可以复用,修改一下链表指针即可。

  min_rec_mask表示记录是否是B+树每层非叶子节点中的最小。

  record_type表示这条记录的类型,0普通记录,1表示B+树的非叶子结点,2最小记录,3最大记录。最大最小记录都是InnoDB自己插的。

  n_owned表示当前记录组的记录条数,为了提高查找效率将记录分组,每组最大的那条记录(根据主键排)的owned_no表示自己组的拥有记录条数。

贴个别人的图,不然太不直观:

InnoDB存储引擎的记录格式,数据页的结构第1张

真实数据的隐藏列

  隐藏列不是我们加上去的数据,是mysql加上去并维护的,但是也属于是真实数据。例如有:DB_ROW_ID,行id唯一标识一条记录;DB_TRX_ID事务id;DB_ROLL_PTR回滚指针等等。

(InnoDB一页16KB,而MySQL限制:不算BLOB、TEXT列,每行大小不超过65535B,因此有可能出现一页装不下一行数据的i情况。compact处理这种情况就是能放进的数据照常存放,然后用这一页的最后20B记录剩下的数据存放在哪些页哪些位置,相当于地址加偏移)

MySQL8中InnoDB的默认行格式是Dynamic,它与compact类似,只是在行溢出的时候有一点不同,只要这一行数据大小超过16KB,就直接用20B记录存放数据的页和位置,想当于这行的真实数据一点也不留点放在当前页。

InnoDB页的结构

  InnoDB页有很多种类型:索引页(存放实际数据),undo日志信息页,insertBuffer页,inode结点信息页。重点了解索引页

InnoDB存储引擎的记录格式,数据页的结构第2张

  User Records存放记录,插入新记录可以从Free Space分配内存,然后这部分内存就属于User Records,当Free Space不足时就需要下一页了。

Page Directory相当于目录,由一组槽构成,每个槽记录的是对应记录组最大记录的地址,0号槽的记录排序最低,槽号越大记录排序越高。初始情况系统添加的最大记录、最小记录分别在两个槽中,之后每插入一条记录都找到排序和要插入记录最相近的槽,当一个槽达到8个后再插入就直接转变为两个槽,一个4条一个5条。当查找记录时根据槽号二分查找,可以快速找到目标槽,然后去这个槽里的那组记录里遍历链表查找。总之,Page Directory加快了记录的查找速度。

Page Header页面头部,这部分保存了整个页面的状态信息,比如本页中存储了多少记录、多少槽、当前页在b+树的层级、索引ID表示当前页属于哪个索引等。

File Header文件头部,这里记录一些更通用的状态信息,而不是适用某种类型。页号、上一页页号、下一页页号、页面类型等等、属于哪个表、页校验和等等。

File Tralier文件尾部,也是记录页面通用信息;前4B记录了整个页的校验和,当页在内存中修改后就会生成新的校验和,然后写入磁盘时先写入File Header的校验和最后写入File Trailer的校验和,如果某次写入磁盘因为断电等原因没有成功写完,则两个地方的校验和将不一致,最后读取一个页发现头尾的校验和不一致就能发现出错了;后4B则代表最后一次修改时对应的日志序列位置。

免责声明:文章转载自《InnoDB存储引擎的记录格式,数据页的结构》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Window环境下安装Python2和Python3  OA发展史:由点到生态下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySQL单表最大限制

  想把一个项目的数据库导出来,然后倒入到自己熟悉的MySQL数据库中进行运行和调试。导出来后,发现sql文件整整有12G多大,忽然想起来,MySQL好像有个叫做容量限制的神奇特性,但是忘了上限是多少了,所以查阅资料得出了如下结果:   在老版本的 MySQL 3.22 中,MySQL的单表限大小为4GB,当时的MySQL的存储引擎还是ISAM存储引擎。但...

MYSQL常用命令集合(转载)

文章出处:http://www.cnblogs.com/q1ng/p/4474501.html 1.导出整个数据库mysqldump -u 用户名 -p --default-character-set=latin1 数据库名 > 导出的文件名(数据库默认编码是latin1)mysqldump -u wcnc -p smgp_apps_wcnc >...

MySQL5.0、5.1、5.5、5.6功能进化

目前线上使用的版本情况:新上线端口统一使用5.5,不说别的,一个快速恢复重启就值回票价。但因为历史原因还有大量5.1的版本,甚至,I’am sorry,还有少数5.0的版本。至于5.0以前的版本,你是从唐朝穿越过来的吗? 3.23 2001 4 2003 5.0 2006 5.0.96GA1,视图,触发器,存储过程和存储函数2,游标3,xa分布式事务2...

《深度剖析CPython解释器》29. Python内存管理与垃圾回收(第二部分):源码解密Python中的垃圾回收机制

楔子 现在绝大部分的语言都实现了垃圾回收机制,这其中也包括Python,而不同的语言采用的垃圾回收算法也各不相同。那么,常见的垃圾回收算法都有哪些呢? 引用计数法(reference count): 记录对象的被引用此处, 引用计数降为0时回收 标记-清除法(mark-sweep): 从根集合触发, 遍历所有能访问到的对象并对其进行标记, 然后将未被标记...

mysql中的data下的数据文件(.FRM、.MYD、.MYI)恢复为数据

记一次mysql中的data文件操作经历 想拿到一个项目的最新的数据,做功能升级使用,备份一份数据同时也作为本地测试数据,文件有些大,我直接通过远程的phpmyadmin程序导出,不能愉快的玩耍,直接联系了IDC的同事帮忙导一份sql文件出来一下,结果那哥们没得直接扔给了我data文件,这让我如何是好,这个问题我没遇见过啊。处于面子问题,又不好意思再让他重...

使用engine关键字指定该表使用哪个engine

建表及插入数据语句:mysql> create table salary(userid int,salary decimal(9,2));Query OK, 0 rows affected (0.11 sec)mysql> insert into salary values(1,1000),(2,2000),(3,3000),(4,4000),...