关于mysql:MySQL系列InnoDB行记录存储结构

42次阅读

共计 2900 个字符,预计需要花费 8 分钟才能阅读完成。

前言

咱们平时在向 MySQL 数据库表中插入数据时,理论数据是以行记录的格局存储在磁盘上的,本篇咱们就一起来具体的理解下 MySQL 的行记录格局,了解了行记录的格局有助于咱们前面理解 MySQL 如何疾速在页中定位出行记录,以及 MySQL 的版本控制链,事务隔离级别等等,行记录格局是许多 MySQL 外围常识的根底。

InnoDB 行记录类型

MySQL 中总共提供了四种类型的行格局:Compact,Redundant,Dynamic,Compressed

在创立表或批改表的时候能够指定行记录的格局
create table 表名 row_format= 行格局名
alter table 表名 row_format= 行格局名

晓得就行,不须要去记住,基本上应用不到

Compact 行格局

在四种类型的行格局中,咱们次要来学习 Compact 格局,其余格局的行记录相似;

从图中咱们能够看出行记录次要是由 4 局部组成:变长字段长度、Null 值列表,行记录头信息以及列的实在数据

变长字段长度列表

在 MySQL 中很一些变长的数据类型(varchar,text 等),MySQL 须要晓得这些数据的理论长度,这样能力正确的在实在数据中取出对应列的数据,所以变长字段是由两局部组成:

  • 实在数据的长度
  • 实在数据的字节

每个变长字段的长度要么用 1 字节要么用 2 字节示意,由此就决定了每个字段的最大字节数是 65535;

  • 如果字符类型若为 gbk,每个字符最多占 2 个字节,最大长度不能超过 32766;
  • 如果字符类型若为 utf8,每个字符最多占 3 个字节,最大长度不能超过 21845。

那到底什么时候选用 1 字节什么时候选用 2 字节呢?

这里须要定义三个变量:w,m,l

  1. 如果应用的字符集是 utf8mb4,每个字符占用的字节数是 4 字节,那么 w =4;如果字符类型若为 utf8,每个字符最多占 3 个字节,那么 w =3; 所以 w 示意字符集中每个字符所占的字节数
  2. varchr(m),这里 m 示意的是定义的字符的长度
  3. l 示意的是该字段实在数据占用的字节数

m*w <= 255;示意该字段定义的最大长度都不会超过 1 字节,那么该字段的长度就用 1 字节示意

m*w > 255 && l<=127; 示意该字段定义的长度可能会超过 1 个字节,然而以后的理论长度是小于 127 的,能够用 1 个字节示意

m*w > 255 && l>127; 用 2 字节来示意该字段的长度

思考:为什么与 l 比拟的值是 127 呢?
当咱们定义的变长字段可能大于 255(也就是超过一个字节)时,MySQL 如何能力晓得以后读取的字节该字段的实现字段长度,还是该字段的半个字段长度,为了解决这个问题,MySQL 应用了 1 字节的首位,当首位为 0 示意以后是 1 字节,当首位为 0 示意以后长度是 2 字节;因为占用了 1 字节的首位,所以剩下 7 位所能示意的最大值是 127

变长字段不会存储为 Null 列的长度;其次并不是行记录中肯定须要变长字段长度这段内容,如果行记录中没有定义变长字段或者是变长字段都为 Null,那么就不会有变长字段长度这部分

变长字段占用的字节数依照程序逆序存储

Null 值列表

一条记录中某些列通常可能容许为 null,所以 Compact 行格局把这些容许为 null 的进行了对立治理;

  1. 首先统计出表中定义的哪些列容许为 null
  2. 如果表中的字段都不能为空,那么就不存在 null 值列表;如果存在容许为 null 的字段,那么就依照字段的程序为每个字段对应一个二进制位,当二进制位为 1 时示意该列值为空;当二进制位位 0 时示意该列值不为空
  3. Null 值列表必须有整数个字节来示意,所以对应没有占用的位应用 0 补位

行记录的头信息

头信息中次要蕴含了 6 个字段,其中 5 个字段也是在面试中常常被问到的,为了不便记忆,咱们把 5 个字段对应到手的 5 根指头:

  • n_owned(拇指): 一个数据页会被分成很多个组,每组最初的一条记录该字段为 1,其余记录该字段为 0,就像分组中所有的记录的大哥;(对应拇指)
  • deleted_flag(食指): 标记该记录是被删除的;当记录被删除时不会实在删除,而是用该字段标记,并且把所有删除的记录应用链表连接起来,当前的文章会持续说到这个字段。(设想下你平时挖鼻屎是不是用的食指)
  • heap_no(中指): 示意以后记录在数据页中的绝对地位(MySQL 应用该字段来示意记录地位,能够和中指对应,不可形容)
  • record_type(无名指): 示意以后记录属于哪种类型,(无名指用来带戒指的,与分类无关,能够把人分为已婚和未婚,)

    1. 0 示意一般记录
    2. 1 示意目录项记录,索引中非叶子结点中的数据记录都是 1
    3. 2 示意 infrmum 记录,每个数据页中至多会有两条记录,其中最小记录的 record_type=2
    4. 3 示意 Supremum 记录,每个数据页中至多会有两条记录,其中最大记录的 record_type=3
  • next_record(小拇指): 寄存下一条记录的绝对地位(当数数时,左手的小拇指数完之后就该换右手了,和 next_record 表白的意思类型)

最初一个字段 min_rec_flag : B+ 树中每层非叶子结点最小目录项记录该字段为 1;该字段绝对于其余 5 个字段显得不那么重要,不会影响了解 B + 树索引

暗藏列

除了用户自定义的数据列以外,MySQL 还会为每行记录生成 3 个暗藏列

  • row_id: 行 ID,记录的惟一标识;当用户在表中定义了主键字段就优先选择用户定义的主键,如果没有,就查找是否有定义不为 null 的惟一索引,如果有就把该列作为主键,如果没有 MySQL 就会生成一列 row_id 暗藏列作为主键
  • trx_id: 事务的 ID;该字段对于实现一致性视图和事务隔离级别至关重要,当前会具体阐明
  • roll_pointer: 回滚指针,指向的是该记录的上一个版本号,MySQL 的 MVCC 次要就是通过这个字段来实现的。

溢出列

MySQL 中所有的行记录都会被存储在数据页中,每个数据页的大小是 16KB,也就是 16384 个字节;在后面咱们讲过变长字段的长度能够用两个字节来示意,所以列的最大长度能够是 65535,当遇到这种极其状况时,一个数据页是存储不下这一条记录的。

Compact 行格局针对这种状况的解决形式是在实在的数据处记录该列的一部分数据(768 字节),其余多余的数据会存储到新的数据页中(溢出页),而后在该记录中应用 20 个字节存储这些数据页的地址

溢出页与溢出页之间应用的链表相连接

其余的行记录格局:

Redundant:MySQL5.0 之前的格局,间接疏忽

Dynamic,CompressedCompact 很像,只是在溢出列的解决有些差别,他们只会在实在数据列中应用 20 个字节存储溢出页的地址

面试题

  • char(M)定义的字段,在变长字段的长度列表中会记录该字段的长度吗?

欢送大家在评论区留言探讨


最初(点关注,不迷路)

文中或者会存在或多或少的有余、谬误之处,有倡议或者意见也十分欢送大家在评论交换。

最初,写作不易,请不要白嫖我哟 ,心愿敌人们能够 点赞评论关注 三连,因为这些就是我分享的全副能源起源🙏

程序员罕用的 IDEA 插件:https://github.com/silently9527/Toolkit
原文链接:https://silently9527.cn/?p=62

正文完
 0