InnoDB - 页结构

news2024/11/27 13:34:48

InnoDB - 页结构

文章目录

  • InnoDB - 页结构
    • 1. InnoDB页简介
    • 2. InnoDB页结构
      • 2.1 User Records(数据)
      • 2.2 Page Directory(页目录)
      • 2.3 Page Header(页头部信息)
      • 2.4 File Header(文件头)
      • 2.5 File Trailer(文件尾)
    • 3. 总结

在阅读本文章之前,你需要了解的前置知识:

  • InnoDB - 行格式

1. InnoDB页简介

InnoDB是一个将表中的数据存储到磁盘上的存储引擎,所以即使关机后重启我们的数据还是存在的。而真正的对数据的处理过程是发生在内存中的。

即:将磁盘中的数据读取到内存中进行操作。

  • 读请求:将磁盘中的内容读取到内存中。
  • 写请求:将内存中的被修改后的数据写回磁盘中。

我们知道,读写磁盘的速度非常慢,与内存操作差了几个数量级,所以当我们想要从表中获取数据时,InnoDB会一条一条的把记录从磁盘中都出来吗?不是,InnoDB采取的方式是:将数据划分为若干页,以页为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为16k。

在这种情况下,MySQL一次最少会从磁盘中加载16k的数据到内存中,说明白点:即使你只select了一条数据,与该条数据同一页的所有数据都会被加载(从磁盘加载到内存)。

现在我们就可以梳理一下MySQL的InnoDB引擎中页的概念:InnoDB管理存储空间的基本单位、用于磁盘与内存交互的最小单位。

2. InnoDB页结构

16k的页被分为好多个部分,并不是16k全部存储数据的。以下为页的基本结构。

0

共有7个部分,他们的大概功能如表格所示:

你可以先看表格了解一下它们都是干啥的,下面会详细的描述它们的作用。

名称中文名作用
File Header文件头部页的一些通用信息
Page Header页面头部数据页专用的一些信息
Infimum + Supermum最小记录和最大记录两个虚拟的行记录
User Records用户记录实际存储的行数据内容,初始为空,随着记录的增加,从Free Space中挪用空间
Free Space空闲空间页中未使用的空间,为User Records提供空间
Page Directory页面目录页中的某些记录的相对位置
File Trailer文件尾部校验页是否完整

我们接下来并不打算按照页中各个部分的出现顺序来依次介绍它们,因为各个部分中会出现很多大家目前不理解的概念.

2.1 User Records(数据)

image

在页的7个组成部分中,我们自己的数据(student、user)会存储在User Records中。但是在一开始生成页的时候不会为User Redords分配空间,每当我们插入一条数据,都会从Free Space分出部分空间到User Redords来存储这条数据,直到Free Space被User Records全部替代,说明这个页用光了,就会创建新的页。

image

在上一篇InnoDB - 行格式中,我们介绍了InnoDB行格式,说到了每一行其实有6个隐藏字段,其中有一个隐藏字段叫做:记录头信息。

image

记录头信息中又有很多字段,在这里会涉及到的有5个:(此图省略了其他隐藏字段)

image

delete_mask:该记录是否被删除。

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

n_owned:有几个记录属于这个记录。每个组的最后一条记录(也就是组内最大的那条记录)的头信息中的 n_owned 属性表示该记录拥有多少条记录。

录,也就是该组内共有几条记录。

heap_no:在页中的下标(第几条数据)。

next_record:表示下一条记录的相对位置。

我们想这张表中插入3条数据:

insert into users values(1, 'aaaa'), 
                        (2, 'bbbb'),                       
						(3, 'cccc');

于是这几条数据就如下图所示连接:

image

由于还没有接触到,所以有几个字段在图中是不重要的,这里只需要注意两个字段:heap_no、next_record。

很明显,next_record是指向下一条记录的指针。

heap_no是此行数据在页中的下表,但是我们知道下标一般是从0或者1开始的,哪有从2开始的呢?怎么不见 heap_no 值为 0 和 1 的记录呢?

其实MySQL的设计者在实现这部分的时候耍了花样:页结构中有这样一个字段:Infimum、supermum。它们的中文名分别是最小记录和最大记录。

现在你应该猜到序号0和1都归谁了吧?就是最小记录和最大记录。

image

那么刚才那张图完善一下就是:

image

如图所示,MySQL隐藏的最小记录指向我们添加的主键最小的记录,形成一个链表,最后我们添加的主键最大的记录指向MySQL自带的最大记录。

即:infimum -> User Records -> supermum。

从中删除一条数据,整个链表的结构就会发生变化,假如从中删除id为1的数据,

image

不论我们怎么对页中的记录做增删改操作,InnoDB始终会维护一条记录的单链表,链表中的各个节点是按照主键值由小到大的顺序连接起来的。

当数据页中存在多条被删除掉的记录时,这些记录的next_record属性将会把这些被删除掉的记录组成一个垃圾链表,以备之后重用这部分存储空间。

即:未删数据和删除数据都会组成链表。

你会不会觉得next_record很奇怪?它竟然指向隐藏字段和真实数据之间的部分,而不是指向隐藏字段开头部分

因为这个位置刚刚好,向左读取就是记录头信息,向右读取就是真实数据。

我们知道,隐藏字段中信息的记录都是逆序的(参考InnoDB - 行格式),它从右向左读就可以读取到正序的信息了!

2.2 Page Directory(页目录)

image

刚才说了,数据之间会形成链表,方便查找,但是链表很明显效率很慢。MySQL的索引使用的是B+树,这里我们讲的是数据页,不是索引页,所以没有用到B+树,但是MySQL的设计者还是采用了一定的方式加快数据的查询。

也就是查目录的方式。

打一个比较形象的比喻:

进了大学,班长记不住也不想记住所有同学的名称,于是他将全班同学按照学号排序、分组,每个组大概4-8人,组里学号最大的那个人当组长,班长建立一个组长群,将组长拉进去,有啥事就直接通知组长而不会通知组员,想要找某个人的时候就直接在组长群里说:xxx在哪一个组?组长叫他做一下青年大学习~

MySQL采用了以下方式:

  1. 将所有未删除的数据划分为不同的组
  2. 每个组的最后一条记录的头信息中的 n_owned 属性表示该组内有多少条记录
  3. 将每个组的最后一条记录的地址偏移量单独提取出来按照顺序存储到 Page Directory 中(也就是页目录),我们将这些地址偏移量称为 槽。

这里不要被搞糊涂了:一个页中有很多条数据,这些数据会按照链表的格式存储,我们将这些数据分为很多组,每个组的最后一条记录

的地址抽取出来记录在 Page Directory 中。

这里的整个班就是一个页,各个组就是MySQL中的组,组长就是每个组的最后一条数据,组长群就是 Page Directory。进入组长群后的组长被称为“槽”。

image

注意:

  • 槽0中的数字为91,代表最小记录的地址偏移量为91字节。
  • 槽1中的数字为122,代表最大记录的地址偏移量为122字节。
  • 槽0指向最小记录,代表最小记录单独一组,也就只有最小记录这一条数据,所以它的n_owned值为1。
  • 槽1指向最大记录,代表最大记录以及之前的数据为一组,所以它的n_owned为4

那么为什么最小记录单独一组呢?因为规定,MySQL对于每个分组中的记录个数是有规定的:最小记录所在的分组只能由1条记录,最大记录所在的分组可以有18条记录,剩下的其他分组可以有48条记录。

我们看一下数据多的时候:

image

如图所示,当有15条数据时,总记录数为17,被分为5个组,共有5个槽位,槽位对应的数据条数分别为:1、4、4、4、3。

但是!插入的时候是有讲究的!

  • 初始情况下是没有用户数据的,只有两个组:最小记录组与最大记录组。
  • 之后没插入一条记录,都会从页目录(槽位)中找到主键值比本记录大,并且差值最小的槽位,然后把该槽位指向的记录的 n_owned 值加一,表示本组内又添加了一条记录,直到该组中的记录数等于8个。
  • 当组中的记录数等于8个时,再次插入会将本组分为两个组,加入分为4组和5组,第四组有4条记录,第五组有5条记录。因为拆分了,所以页目录中会增加一个槽位。

弄清了分组的情况,现在我们可以来查找了,假如想要查找到 id=8 的数据。从图中可以看到它在第三组。

因为各个槽指向的记录的主键都是从小到大排列的,所以我们可以使用二分法。

此时共有5组,编号为 0、1、2、3、4 ,记 low = 0,high = 4

  1. (low + high)/ 2 = 2,第二个槽位对应的记录的id为9,太大了,high改为2
  2. (low + high)/ 2 = 1,第一个槽位对应的记录的id为5,太小了,low改为1
  3. 因为 high - low = 1,可以说明目标数据就在 第二组 中,我们只需要获取low槽位对应的数据(第一组的最后一个数据),向下找一个数据就到达第二组的最小数据,此时high指向的槽位的记录数是第二组的最大记录,获得了最大与最小,id=8的数据还不是唾手可得?

所以在一个数据页中查找指定主键值的记录的过程分为两步:

  • 通过二分法确定该记录所在的槽,并找到改槽中主键值最小的那条记录。
  • 通过记录的 next_record 属性遍历该槽,找到目标数据。

2.3 Page Header(页头部信息)

image

数据页头部信息,行数据字段记录本行的基本信息,那么页数据也会有专门的字段记录本页的信息,而Page Header就是用于记录一个数据页的状态信息,比如本页存储了多少个槽、多少个数据等等…

具体有什么字段,来看表格:(由于字段太多了,这是精简过的字段)

名称作用
Page_N_Dir_Slots该页的页目录中的槽的数量
Page_N_Heap本页中的记录的数量(删除&未删除)
Page_Level当前页在B+树所处的层级
Page_Index_id本页属于哪个索引

2.4 File Header(文件头)

image

File Header是每种页都有的属性,其中重要的字段如:本页编号、上一页编号、下一页编号、校验和。

上一个与下一个页的编号:可以将所有数据页组成一个双链表,遍历就很方便了。

File Header中有一个很重要的字段:校验和。

什么是校验和?

对于一个很长很长的字符串来说,我们会使用某种算法来计算一个比较短的字符串来代替它,比如我们通常用文件的md5编码来初步比较文件是否相等。

我们想比较两个很长的字符串的时候,可以先比较他们的校验和,如果连校验和都不一样,那这两个字符串就肯定不一样。这样就省去了一个字符一个字符比较消耗的时间。

同时,File Header的校验和还会和File Trailer的校验和配合工作,下面会介绍。

2.5 File Trailer(文件尾)

image

File Trailer跟File Header一样,有一个校验和,那么这个校验和到底是什么作用呢?

我们知道InnoDB存储引擎会将数据从磁盘中读到内存中,以页为单位。如果数据在内存中修改了,那么MySQL势必要把他们再写回磁盘中,写回的过程中万一出现了什么错怎么办?这不是尴尬了吗?为了检验一个页是否完整(也就是同步的时候是否出现同步了一半的情况),MySQL就在页的头部和尾部都加了校验和这个东西。

每当一个页在内存中修改了,在同步之前就要把它的校验和算出来。File Header在页的最前面,所以校验和会被优先同步,如果完全写完,那么尾部的校验和也会写进去,这没问题。万一中断了,那么File Header的校验和是已经修改过的页,File Trailer的校验和是原先的页,二者不相同,说明同步过程中出现bug。

(其实还有一个字段,现在先不管)

3. 总结

  1. InnoDB为了不同的目的而设计了不同的页类型,我们把存放数据的页称为数据页。

  2. 数据页可以被分为7个部分:

    1. File Header:表示页的一些通用信息
    2. Page Header:表示数据页专有的信息
    3. Infimum + Supermum:两个虚拟的伪记录,分别表示页中的最小记录和最大记录
    4. User Records:存放我们插入的数据
    5. Free Space:页中可以被我们使用的数据
    6. Page Directory:页中的某些记录的相对位置,其中存放很多槽位
    7. File Trailer:页尾,与File Header一起提供校验和来检查同步状态
  3. 每个数据页的File Header都有上一个与下一个页的编号,所以所有的数据页会组成一个双链表

  4. User Records中的每一条记录有next_record,它将所有记录串联为一个链表(已删除的和未删除的,共两个链表)

  5. 为保证内存到磁盘的同步的完整性,在数据页的首部和数据页的尾部都会存储页中数据的校验和

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ByteBuddy

JavaAgent是在JDK5之后提供的新特性,也可以叫java代理。 开发者通过这种机制(Instrumentation)可以在加载class文件之前修改方法的字节码(此时字节码尚未加入JVM),动态更改类方法实现AOP,提供监控服务如;方法调用时长、可用率、内…

RocketMQ详解

介绍 ​ RocketMQ作为一款纯Java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。 RocketMQ 特点 支持发布/订阅(Pub/Sub)和点对点(P2P)消息模型 ​ 在一个队列中可…

ASEMI代理英飞凌SPW47N60C3功率MOS管的性能与应用

编辑-Z 本文将对SPW47N60C3功率MOSFET的性能与应用进行分析。我们将介绍SPW47N60C3的基本性能参数,然后从四个方面对其性能进行详细阐述,包括电气性能、热性能、封装与可靠性以及应用领域。 1、SPW47N60C3的基本性能参数 SPW47N60C3是一款高性能的功率…

Python 和 C++ 混合编程:pybind11 使用

Python 和 C 混合编程:pybind11 使用 1. 目的需求2. pybind 11 简介3. 使用实践3.1 开发环境3.2 准备工作3.3 建立工程 test3.4 编译工程 test 库3.5 Python 调用 工程 test 库 4. 实线小结5. 参考资料 1. 目的需求 我们通常在 Python 上进行算法开发,因…

【数据结构】——树的相关习题

目录 一、选择填空判断题题1题2题3题4题5题6题7题8题9 二、应用题题10(遍历序列)题11(存储结构)题12 13(二叉树/树、森林之间的转换)题14(线索二叉树) 一、选择填空判断题 题1 1、设…

Java的包装类(装箱和拆箱面试题)

1.为什么要有包装类(或封装类) 为了使基本数据类型的变量具有类的特征,引入包装类。 2.基本数据类型与对应的包装类: 3.装箱和拆箱 基本数据类型包装成 包装类的实例—装箱 通过包装类的构造器实现: int i 500; Integer t ne…

案例34:基于Springboot在线互动学习网站开题报告设计

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

深度学习笔记之Seq2Seq(一)基本介绍

深度学习笔记之Seq2seq——基本介绍 引言回顾:经典循环神经网络结构关于循环神经网络的更多引用 Seq2seq \text{Seq2seq} Seq2seq网络结构 Seq2seq \text{Seq2seq} Seq2seq结构描述 引言 从本节开始,将介绍 Seq2seq \text{Seq2seq} Seq2seq。 回顾&…

苏轼人生历程不同时期作品

大文豪苏轼才华横溢,一生却屡经贬谪,四处漂泊。但好在他生性豁达,为人率真,又深得道家思想精髓,所以他不仅没有为不幸的遭遇所折服,反而活出了别样的潇洒与风采。也正因如此,他的诗词、他的性情…

人脸融合相关调研

以前的记录,整理发现了值得分享一下。 云解决方案 腾讯: https://ai.qq.com/product/facemerge.shtmlFace: https://www.faceplusplus.com.cn/face-merging/Baidu:https://ai.baidu.com/tech/face/merge 自研 纯Opencv实现,基于…

由世纪互联运营的Microsoft Teams携创新功能正式发布,助力合作伙伴生态共赢

2023年4月18日,北京——由世纪互联运营的Microsoft Teams推出一系列创新功能,围绕企业数字核心能力,赋能数字化协作空间、智能化协作体验、整合生产力工具和工作流、安全合规、构建团队文化等五大落地场景,这将进一步帮助中国市场…

【unity造轮子】最简单的双向可穿越平台制作(Platform Effector 2D的使用)

文章目录 先看最终效果2D平台效应器(Platform Effector 2D)1. 这里简单解释一下2.一些常见的效果包括:3.Platform Effector 2D具有各种属性,使我们可以控制它的行为。这些属性包括: 可向上穿越的平台向下穿越的平台最终…

代码随想录算法训练营第一天| 704.二分查找、27.移除元素

LeetCode704.二分查找 链接:(704.二分查找) 题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 题目就…

远程服务调用的简单应用,并轻松解决LinkedHashMap无法转成相关实体类的问题

🏀(一)为啥需要远程服务调用? 🐠知其然还要知其所以然,在我们的生产项目上一般而言会部署多个微服务,每个微服务会负责不同版块的业务工作。如果某个微服务需要借助另外的某些微服务中的接口才能…

尚硅谷甄选

搭建后台管理系统模板 项目初始化 今天来带大家从0开始搭建一个vue3版本的后台管理系统。一个项目要有统一的规范,需要使用eslintstylelintprettier来对我们的代码质量做检测和修复,需要使用husky来做commit拦截,需要使用commitlint来统一提…

tb6612电机驱动与JGB37-520减速直流电机

tb6612电机驱动与JGB37-520减速直流电机 文章目录 tb6612电机驱动与JGB37-520减速直流电机电机驱动模块TB6612TB6612的引脚说明真值表(直流电机的驱动状态)TB6612的正转反转原理 直流电机原理减速器编码器一、关于编码器的介绍二、编码器的工作原理&…

Linux:DHCP服务

我的DHCP服务器 是 centos7 使用的yum安装DHCP服务(如果不会搭建yum库可以查看) Linux:rpm查询安装 && yum安装_鲍海超-GNUBHCkalitarro的博客-CSDN博客 我的环境为: 两台centos7一台win10 第一台centos7 为 DHCP服务器…

【Python 私有变量和私有方法】零基础也能轻松掌握的学习路线与参考资料

Python是一种面向对象的编程语言。在Python类中,私有变量和私有方法是常见的概念。私有变量和私有方法是指只能在类内部访问的变量和方法。在本文中,我们将介绍Python私有变量和私有方法的学习路线,并给出参考资料和优秀实践。 学习Python面…

【MySQL数据库 | 第七篇】图形化界面工具DataGrip基础应用教学

目录 前言: DataGrip安装界面: 利用DataGrip创建数据库: 利用DataGrip为数据库创建表: 利用datagrip修改表: 添加元素: 结束! 前言: 在之前我们一直接触的是MySQL命令行语句开…

macOS Sonoma 14beta With OpenCore 0.9.3 and winPE双引导分区黑苹果原版镜像

镜像特点(原文地址:http://www.imacosx.cn/113888.html) 完全由黑果魏叔官方制作,针对各种机型进行默认配置,让黑苹果安装不再困难。系统镜像设置为双引导分区,全面去除clover引导分区(如有需要…