前言: 页是InnoDB管理存储空间的基本单位,上一章我们主要分析了页中的主要的构成行的存储结构-行格式,其中简单提了一下的概念。这章我们详细讲解一下页的存储结构。

一、数据页结构

前边我们简单提了一下的概念,它是InnoDB管理存储空间的基本单位,一个页的大小一般是16KB。和存储一条条数据的行有多种行格式类似,InnoDB为了不同的目的而设计了许多种不同类型的,比如存放表空间头部信息的页,存放Insert Buffer信息的页,存放INODE信息的页,存放undo日志信息的页等。

本章我们主要聚焦的是那些存放我们表中记录的那种类型的页,官方称这种存放记录的页为索引(INDEX)页,我们也可以称之为数据页。数据页代表的这块16KB大小的存储空间可以被划分为多个部分,不同部分有不同的功能,各个部分如图所示:

数据页的数据结构主要为存储数据服务而设计的,所以讲解数据页存储结构先要从数据插入讲起。

二、记录在页中的存储(infimum+Supermum & User Records & Page Directory)

2.1 infimum+Supermum & User Records

我们自己存储的记录会按照我们指定的行格式存储到User Records部分。但是在一开始生成页的时候,其实并没有User Records这个部分,每当我们插入一条记录,都会从Free Space部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。

上图中的行格式我们省略了上一章的记录头的额外信息(上一章我们已经着重讲了行格式的可变长字段长度列表,NULL值列表和真实数据,基本根据存储数据是不变的),本章重点展示了记录头信息,因为它存储信息和页结构有很大关联。

  • delete_mask 这个属性标记着当前记录是否被删除,占用1个二进制位,值为0代表记录并没有被删除,为1代表记录删除。为了节省重排的时间,实际删除一条数据后并没有立即从磁盘删除,所有被删除掉的记录都会组成一个所谓的垃圾链表,在这个链表中的记录占用的空间称之为所谓的可重用空间,之后如果有新记录插入到表中的话,可能把这些被删除的记录占用的存储空间覆盖掉。记录被我们删掉后,在一定时间内如果我们再次把这条记录插入到表中,InnoDB并没有因为新记录的插入而为它申请新的存储空间,而是直接复用了原来被删除记录的存储空间。
  • min_rec_mask B+树的每层非叶子节点中的最小记录都会添加该标记,什么是个B+树?什么是个非叶子节点?后续第六章索引会介绍到这个问题。
  • n_owned 这个数值待会在第五章讲到page directory会详细介绍,主要是为了方便数据的快速查找,数据页存储的一行行数据是按照主键从小到大排序的,查找快速就是分块(4-8条),然后根据二分查找快速定位到每一块数据,再在块中遍历查找。n_owned代表的就是当前这一块数据的条数。
  • heap_no 这个属性表示当前记录在本中的位置,从图中可以看出来,我们插入的4条记录在本中的位置分别是:2345。最小记录和最大记录的heap_no值分别是01,也就是说它们的位置最靠前,这两条记录不是我们自己定义的记录,所以它们并不存放在User Records部分,他们被单独放在一个称为Infimum + Supremum的部分。
  • record_type 这个属性表示当前记录的类型,一共有4种类型的记录,0表示普通记录,1表示B+树非叶节点记录,2表示最小记录(infimum),3表示最大记录(Supermum )。从图中我们也可以看出来,我们自己插入的记录就是普通记录,它们的record_type值都是0,而最小记录和最大记录的record_type值分别为23。至于record_type1的情况,我们之后在说索引的时候会重点强调的。
  • next_record 它表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量,这其实是个单链表,可以通过一条记录找到它的下一条记录。下一条记录指得并不是按照我们插入顺序的下一条记录,而是按照主键值由小到大的顺序的下一条记录。而且规定 Infimum记录(也就是最小记录) 的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录就是 Supremum记录(也就是最大记录) 。

2.2 Page Directory(页目录)

现在我们了解了记录在页中按照主键值由小到大顺序串联成一个单链表,那如果我们想根据主键值查找页中的某条记录,最笨方法就是从Infimum记录(最小记录)开始,沿着链表一直往后找,这时间复杂度就是O(n)。这样数据量大的话是很费时的,所以mysql研发者就加入了目录的设计(Page Directory):

  1. 将所有正常的记录(包括最大和最小记录,不包括标记为已删除的记录)划分为几个组。对于最小记录所在的分组只能有 1 条记录(也就是infimum一个组),最大记录所在的分组(也就是maxmum所在的最后一个组)拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能在是 4~8 条之间。
  2. 之后每插入一条记录,都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽,然后把该槽对应的记录的n_owned值加1,表示本组内又添加了一条记录,直到该组中的记录数等于8个。
  3. 将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到靠近的尾部的地方,这个地方就是所谓的Page Directory,也就是页目录)。页面目录中的这些地址偏移量被称为(英文名:Slot),所以这个页面目录就是由组成的。

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

  1. 通过二分法确定该记录所在的槽,并找到该槽所在分组中主键值最小的那条记录。

  2. 通过记录的next_record属性遍历该槽所在的组中的各个记录。

三、文件的头尾信息(File Header & Page Header & File Tailer)

3.1 File Header

File Header针对各种类型的页都通用,也就是说不同类型的页都会以File Header作为第一个组成部分,它描述了一些针对各种页都通用的一些信息,比方说这个页的编号是多少,它的上一个页、下一个页是谁之类, 这个部分占用固定的38个字节,是由下边这些内容组成的:

  • FIL_PAGE_SPACE_OR_CHKSUM

    这个代表当前页面的校验和(checksum)。啥是个校验和?就是对于一个很长很长的字节串来说,我们会通过某种算法来计算一个比较短的值来代表这个很长的字节串,这个比较短的值就称为校验和。这样在比较两个很长的字节串之前先比较这两个长字节串的校验和,如果校验和都不一样两个长字节串肯定是不同的,所以省去了直接比较两个比较长的字节串的时间损耗。

  • FIL_PAGE_OFFSET

    每一个都有一个单独的页号,就跟你的身份证号码一样,InnoDB通过页号来可以唯一定位一个

  • FIL_PAGE_TYPE

这个代表当前的类型,我们前边说过,InnoDB为了不同的目的而把页分为不同的类型,我们上边介绍的其实都是存储记录的数据页,其实还有很多别的类型的页,如日志页,系统页,事务系统页,索引页(数据页)。。。

  • FIL_PAGE_PREVFIL_PAGE_NEXT

我们前边强调过,InnoDB都是以页为单位存放数据的,有时候我们存放某种类型的数据占用的空间非常大(比方说一张表中可以有成千上万条记录),InnoDB可能不可以一次性为这么多数据分配一个非常大 的存储空间,如果分散到多个不连续的页中存储的话需要把这些页关联起来,FIL_PAGE_PREVFIL_PAGE_NEXT就分别代表本页的上一个和下一个页的页号。这样通过建立一个双向链表把许许多多的页就都串联 起来了,而无需这些页在物理上真正连着。需要注意的是,并不是所有类型的页都有上一个和下一个页的属性,不过我们的数据页(也就是类型为FIL_PAGE_INDEX的页)是有这两个属性的,所以所 有的数据页其实是一个双链表。

3.2 Page Header

InnoDB的设计者为了能得到一个数据页中存储的记录的状态信息,比如本页中已经存储了多少条记录,第一条记录的地址是什么,页目录中存储了多少个槽等等,特意在页中定义了一个叫Page Header的部分,它是结构的第二部分,这个部分占用固定的56个字节,专门存储各种状态信息。字段罗列一下(不需要记忆):

  • PAGE_DIRECTION

    假如新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边。用来表示最后一条记录插入方向的状态就是PAGE_DIRECTION

  • PAGE_N_DIRECTION

    假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记下来,这个条数就用PAGE_N_DIRECTION这个状态表示。当然,如果最后一条记录的插入方向改变了的话,这个状态的值会被清零重新统计。

3.3 File Tailer

  File Trailer主要是为了解决页数据在内存修改后同步到磁盘过程中发生错误导致数据不一致问题,这个部分由8个字节组成,可以分成2个小部分:

  • 前4个字节代表页的校验和

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

  • 后4个字节代表页面被最后修改时对应的日志序列位置(LSN)

    这个部分也是为了校验页的完整性的,只不过我们目前还没说LSN是个什么意思,所以大家可以先不用管这个属性。

文章知识点与官方知识档案匹配,可进一步学习相关知识
MySQL入门技能树首页概览57509 人正在系统学习中

[转帖]深入理解mysql-第五章 InnoDB记录存储结构-页结构的更多相关文章

  1. MySQL InnoDB 索引 (INDEX) 页结构

    MySQL InnoDB 索引 (INDEX) 页结构 InnoDB 为了不同的目的而设计了不同类型的页,我们把用于存放记录的页叫做索引页 索引页内容 索引页分为以下部分: File Header:表 ...

  2. [转帖]深入理解 MySQL—锁、事务与并发控制

    深入理解 MySQL—锁.事务与并发控制 http://www.itpub.net/2019/04/28/1723/ 跟oracle也类似 其实所有的数据库都有相同的机制.. 学习了机制才能够更好的工 ...

  3. 深入理解Magento - 第五章 Magento资源配置

    对于任何一个更新频繁的项目来说,保持开发环境和生产环境的数据库同步是件很头疼的事情.Magento提供了一套系统,用版本化的资源迁移脚本来解决这个问题. 上一章,我们为 Helloworld Blog ...

  4. 高性能mysql 第五章 索引部分总结

    高性能索引 1.索引基础:索引的作用类似'目录'帮助Query来快速定位数据行. 1.1索引类型: 1.1.1 b-tree索引 b-tree(balance tree)索引:使用平衡树(非平衡二叉树 ...

  5. 深入理解PHP内核(五)变量及数据类型-变量的结构和类型

    原文链接:http://www.orlion.ga/238/ 编程语言的类型可以分为强类型和弱类型两种,PHP是弱类型语言,但是C语言是强类型语言.在官网PHP实现内部,所有变量使用同一种数据结构(z ...

  6. 第五章:C++程序的结构

    主要内容: 1.作用域与可见性 2.对象的生存期 3.数据与函数 4.静态成员 5.共享数据的保护 6.友元 7.编译预处理命令 8.多文件结构和工程 作用域:函数原型作用域.块作用域.类作用域.文件 ...

  7. oracle基本语句(第五章、数据库逻辑存储结构管理)

    1.使用SYS用户以SYSDBA身份登录到SQL Plus,使用视图V$TABLESPACE查看表空间信息 SELECT * FROM V$TABLESPACE; 2.查看视图DBA_TABLESPA ...

  8. 【Mysql】InnoDB 引擎中的数据页结构

    InnoDB 是 mysql 的默认引擎,也是我们最常用的,所以基于 InnoDB,学习页结构.而学习页结构,是为了更好的学习索引. 一.页的简介 页是 InnoDB 管理存储空间的基本单位,一个页的 ...

  9. 第五章 MySQL事务,视图,索引,备份和恢复

    第五章 MySQL事务,视图,索引,备份和恢复 一.事务 1.什么是事务 事务是一种机制,一个操作序列,它包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求.要么都执行 ...

  10. Android开发艺术探索第五章——理解RemoteViews

    Android开发艺术探索第五章--理解RemoteViews 这门课的重心在于RemoteViews,RemoteViews可以理解为一种远程的View,其实他和远程的Service是一样的,Rem ...

随机推荐

  1. Redis配置模式及各自优缺点

    大家好,我是闲者,今天简单聊下redis部署模式.Redis支持多种不同的数据结构和模式,以满足不同的使用场景.以下是一些常见的Redis配置和模式示例以及详解.原文地址:Redis配置模式及各自优缺 ...

  2. 简单介绍JDK、JRE、JVM三者区别

    简单介绍JDK vs JRE vs JVM三者区别 文编|JavaBuild 哈喽,大家好呀!我是JavaBuild,以后可以喊我鸟哥,嘿嘿!俺滴座右铭是不在沉默中爆发,就在沉默中灭亡,一起加油学习, ...

  3. flutter常用的音乐播放器库

    audioplayers: 优势:audioplayers 是一个简单易用的音乐播放器库,支持主流平台(Android.iOS)并提供了丰富的功能,比如播放.暂停.快进.音量控制等. 缺点:audio ...

  4. 在xml中比较运算符

    SQL 中,可以使用比较运算符来比较两个值,如使用小于运算符 < 比较两个值大小.但是,在 SQL 查询中,有时候需要将小于运算符 < 用于 XML 或 HTML 语法中,这会导致语法冲突 ...

  5. 微信小程序中业务域名的配置

    需要配置业务域名,需要先将域名http转https 1,首先在该微信小程序的公众号平台中下载 业务域名的证书 2,将下载的证书放进nginx的根目录 3,在nginx中 (root为.txt的地址)

  6. 打通Rasa Action Server和LLM接口的尝试方法

      本文使用最简单的方法对打通 Rasa Action Server 和 LLM 接口进行了尝试,即当 Rasa 对话 intent 为 out_of_scope 时,调用 action_gpt_fa ...

  7. 【API 进阶之路】做 OCR 文字识别,谁说必须要有 AI 工程师?

    摘要:有些功能还真不能光凭自己的直觉和认识,来自一线的声音才是最真实的用户需求.比方说名片录入的需求. 在公司技术委员会副主席这个位置上干了有几个月了,期间,我一方面给研发团队整理各种文档资料,做技术 ...

  8. 基于DAYU的实时作业开发,分分钟搭建企业个性化推荐平台

    摘要:搭建这个平台最费时耗力的事莫过于对批.流作业的编排,作业组织管理以及任务调度了.但是这一切,用DAYU的数据开发功能几个任务可通通搞定. 大多数电商类企业都会搭建自己的个性化推荐系统,利用自己拥 ...

  9. 云图说|AppCube零代码,开启无码新生活

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 应用魔方 App ...

  10. 多语言ASR?没有什么听不懂,15种语言我全都要

    摘要:在这篇博文中,我们介绍来自Google的一篇论文<Scaling End-to-End Models for Large-Scale Multilingual ASR>,来看看如何构 ...