InnDB是怎么存储数据的

只是做了简单的总结和简述,很多地方并没有那么详尽,参考书本《MySQL是怎么运行的:从根上理解MySQL》

InnDB页的记录结构

InnDB是一个将表中数据存储于硬盘中的存储引擎。与之区别的:Memory(存储于内存中),与之相似的:MylSAM;
在这里插入图片描述
对于InnDB来说,数据是存储于磁盘,但是真正处理数据的过程发生在内存中,所以需要经过磁盘IO将 磁盘中的数据加载到内存。InnDB采取了很优秀的方式:将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。 也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式或者记录格式。InnDB拥有着多种行格式,原理上大体相同,主要探讨COMPACT行格式。

COMPACT行格式

在这里插入图片描述
如图,我们可以把一条记录分为 记录的额外信息记录的真实数据两大部分。

  • 记录的额外信息(简述)
    这部分信息是为了描述记录而不得不添加的额外的一些信息,额外信息分外三类 变长字段长度列表NULL值列表记录头信息
    边长字段长度列表:用于存储可变长(VARCHAR、TEXT)字段的真实数据占用的字节长度,并按照列的顺序逆序存放
    NULL值列表: 顾名思义,存储的即字段可为NULL的列的位置,逆序存放
    (二进制位1时,表示该列位NULL,反之则为0)
    记录头信息: 有多个字段,部分字段介绍如下:
字段名介绍
delete_mask标记记录是否被删除(删除则标记为1)
min_rec_maskB+树每层非叶子节点的最小记录都会添加该标记
n_owned表示到本条记录拥有的记录数
heap_no表示当前记录在堆中的位置序号
record_type表示记录类型,0为普通记录,1为B+树非叶子节点记录(目录记录),2表示最小记录,3表示最大记录
next_record表示下一条记录的位置
  • 记录的真实数据
    对于InnDB来说,真实数据除了我们自己定义的数据字段之外还有2个或3个隐藏字段(取决于我们是否定义了主键或Unique键)
列名描述
row_id行ID,唯一标识记录,当我们定义了主键或者Unique键则不会有该字段
tx_id事务ID,用于确保ACID
roll_poniter回滚指针,同样是用于确保ACID

实际上这几个列的真正名称其实是:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR

其他行格式

除COMPACT之外还有Redundant等行格式,MySQL5.7默认行格式为Dynamic,与COMPACT相似,区别在于针对行溢出处理方式不同。
COMPACT会存储溢出数据的一部分数据(768字节),把剩余的数据分散存储至其他的页面,在记录真实的数据处使用20个字节存储指向这些页的地址。
Dynamic则会把所有的字节都从存储到其他的页面中,只在真实数据处存储其他页面的的地址

InnDB的数据页结构

数据页代表的就是这块16KB大小的存储空间
在这里插入图片描述

File Header

文件头部针对各种类型的页都通用,它描述了一些针对各种页都通用的一些信息,比方说这个页的编号是多少,它的上一个页、下一个页是谁。
在这里插入图片描述

Page Header

存储各种状态信息,具体各个字节的含义可以参考下方表格:
在这里插入图片描述
对于PAGE_N_DIR_SLOTSPAGE_LAST_INSERT以及PAGE_N_RECS的含义可先看Page Directory理解。

Free Space(User Records Infimum+ supremum)

Free Spcae区域其实就是用于分配User Records区域的内存区域,当Free Space区域内存全部分配完成,则表明该页面数据量已达最大,需要分页。
infimum+supremum区域为最小记录和最大记录的存储区域
在这里插入图片描述
记录头信息中的next_record列 表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量 可以通过一条记录找到它的下一条记录

Infimum记录(也就是最小记录) 的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录就是 Supremum记录(也就是最大记录)
在这里插入图片描述

Page Directory

InnoDB会把页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个槽,存放在Page Directory中,所以在一个页中根据主键查找记录是非常快的,分为两步:

  • 通过二分法确定该记录所在的槽。
  • 通过记录的next_record属性遍历该槽所在的组中的各个记录。

具体的记录槽分配过程这里就不作过多复述了。
在这里插入图片描述

File Trailer

这个部分是和File Header中的校验和相对应的。每当一个页面在内存中修改了,在同步之前就要把它的校验和算出来,因为File Header在页面的前边,所以校验和会被首先同步到磁盘,当完全写完时,校验和也会被写到页的尾部,如果完全同步成功,则页的首部和尾部的校验和应该是一致的。如果写了一半儿断电了,那么在File Header中的校验和就代表着已经修改过的页,而在File Trailer中的校验和代表着原先的页,二者不同则意味着同步中间出了错。
用于校验页的完整性

B+树 索引

通过InnDB页的介绍我们知道各个数据页可以组成一个双向链表,而每个数据页中的记录又按照主键从小到大的顺序组成了单向链表,每个数据页都会为存储在其中的记录生成页目录,通过主键查找记录,可以在页目录中使用二分法快速定位相对应的槽,然后在遍历对应槽中的记录。

InnDB形成B+树索引的过程:

  • 每当为某个表创建一个B+树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引创建一个根节点页面。最开始表中没有数据的时候,每个B+树索引对应的根节点中既没有用户记录,也没有目录项记录。

  • 随后向表中插入用户记录时,先把用户记录存储到这个根节点中。

  • 当根节点中的可用空间用完时继续插入记录,此时会将根节点中的所有记录复制到一个新分配的页,比如页a中,然后对这个新页进行页分裂的操作,得到另一个新页,比如页b。这时新插入的记录根据键值(也就是聚簇索引中的主键值,二级索引中对应的索引列的值)的大小就会被分配到页a或者页b中,而根节点便升级为存储目录项记录的页。

  • 一个B+树索引的根节点自诞生之日起,便不会再移动

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值