[转帖]深入理解mysql-第五章 InnoDB记录存储结构-页结构

深入,理解,mysql,第五章,innodb,记录,存储,结构 · 浏览次数 : 0

小编点评

**数据结构与页目录** * **数据结构**:页目录,包含页号、槽地址、文件类型等信息。 * **页目录**:每个页的存储地址,指向其所在槽的开始地址。 **文件头** * **文件信息**:页号、文件类型、校验和、文件大小等信息。 * **文件尾**:校验和、日志序列位置等信息。 **页尾** * **校验和**:用于校验页的完整性。 * **日志序列位置**:用于记录页的最后修改时间。 **Page Header** * **Page Direction**:记录页插入方向,右或左。 * **N_Direction**:记录页插入顺序,连续插入。 **File Tailer** * **校验和**:用于校验页的完整性。 * **日志序列位置**:记录页最后修改时间。

正文

 前言: 页是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记录存储结构-页结构相似的内容:

[转帖]深入理解mysql-第五章 InnoDB记录存储结构-页结构

前言: 页是InnoDB管理存储空间的基本单位,上一章我们主要分析了页中的主要的构成行的存储结构-行格式,其中简单提了一下页的概念。这章我们详细讲解一下页的存储结构。 一、数据页结构 前边我们简单提了一下页的概念,它是InnoDB管理存储空间的基本单位,一个页的大小一般是16KB。和存储一条条数据的

[转帖]深入理解mysql-第六章 mysql存储引擎InnoDB的索引-B+树索引

一、引入索引 在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,因为要遍历所有的数据页,时间复杂度就是O(n),所以这种方式显然是超级耗时的。所以我们需要采取一定的数据结构来存储数据,方便我们进行数据的增删改

[转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)

目录 一、初识Explain 二、执行计划-table属性 三、执行计划-id属性 四、执行计划-select_type属性 一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采

[转帖]Redis 运维实战 第01期:Redis 复制

https://cloud.tencent.com/developer/article/1986816 作者简介 马听,多年 DBA 实战经验,对 MySQL、 Redis、ClickHouse 等数据库有一定了解,专栏《一线数据库工程师带你深入理解 MySQL》作者。 从这篇文章开始,将出几期 R

[转帖]深入理解mysql-第十一章 mysql查询优化-Explain 详解(中)

一、执行计划-type属性 执行计划的一条记录就代表着MySQL对某个表的执行查询时的访问方法,其中的type列就表明了这个访问这个单表的方法具体是什么,比方说下边这个查询: mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';+ + + + + + +

[转帖]深入理解mysql-第十二章 mysql查询优化-Explain 详解(下)

我们前面两章详解了Explain的各个属性,我们看到的都是mysql已经生成的执行计划,那这个执行计划的是如何生成的?我们能看到一些过程指标数据吗?实际mysql贴心为我们提供了执行计划的各项成本评估指标的以及优化器生成执行计划的整个过程的方法。 一、查看执行计划计算的成本数据 我们上边介绍的EXP

[转帖]MySQL 慢查询日志深入理解

https://www.jb51.net/article/210312.htm + 目录 什么是慢查询日志 MySQL的慢查询日志是 MySQL提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句,具体指运行时间超过long_query_time 值的 SQL,则会被记录到慢查询日

[转帖]Intel PAUSE指令变化如何影响MySQL的性能

https://zhuanlan.zhihu.com/p/581200704 导读 x86、arm指令都很多,无论是应用程序员还是数据库内核研发大多时候都不需要对这些指令深入理解,但是 Pause 指令和数据库操作太紧密了,本文通过一次非常有趣的性能优化来引入对 Pause 指令的理解,期望可以事半

【转帖】mysql一个索引块有多少指针_深刻理解MySQL系列之索引

索引 查找一条数据的过程 先看下InnoDB的逻辑存储结构:node 表空间:能够看作是InnoDB存储引擎逻辑结构的最高层,全部的数据都存放在表空间中。默认有个共享表空间ibdata1。若是启用innodb_file_per_table参数,须要注意每张表的表空间内存放的只是数据、索引和插入缓冲B

[转帖]深入理解同步机制---内核自旋锁

https://switch-router.gitee.io/blog/spinlock/ 进程(线程)间的同步机制是面试时的常见问题,所以准备用一个系列来好好整理下用户态与内核态的各种同步机制。本文就以内核空间的一种基础同步机制—自旋锁开始好了 自旋锁是什么 自旋锁就是一个二状态的原子(atomi