📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 🏳️🌈一、页的大小可以设置吗?
- 🏳️🌈二、页都有哪些分类? 我们需要重点学习哪种页?
- 🏳️🌈三、页头和页尾具体包含了哪些信息?
- 3.1 页头 - File Header
- 3.2 页尾-File Trailer
- 3.3 什么是 LSN
- 3.4 除了页头和页尾,数据页中还有哪些信息?
- 3.5 页主体中包含哪些信息?
- 🏳️🌈四、数据行有哪些信息组成?
- 4.1 数据行是如何组织在一起的?
- 4.2 怎么标识新页中的第一行和最后一行?
- 4.3 当向一个新页插入数据时是如何执行的?
- 🏳️🌈五、如果要查询的数据在某一个页中,如何定位它在页中的位置,一条条遍历吗?
- 5.1 一条条遍历的查询效率高不高?
- 5.2 如何提高页内的查询效率?页目录
- 🏳️🌈六、关于事务、索引这些信息在页中怎么记录?
- 🏳️🌈七、数据页的完整结构是什么样的?
- 👥总结
11111111
11111111
11111111
11111111
**** 11111111
页
在 MySQL
运行的过程中起到了非常重要的作用,为了能发挥更好的性能,可以结合自己系统的业务场景和数据大小,对页相关的系统变量进行调整,页的大小就是一个非常重要的调整项。同时关于页的结构也要有所了解,以后介绍的索引原理也是基于页实现的。
首先来看关于页的几个问题。
🏳️🌈一、页的大小可以设置吗?
- 前面介绍了每个数据页默认为
16KB
,是操作系统"数据块"4KB的整数倍,那么只要保证页的大小是操作系统"数据块"大小的整数倍是不是也可以呢,答案是肯定的。 - MySQL提供了一个专门的系统变量来控制页的大小,可以通过系统变量
innodb_page_size
进行调整与查看,在调整页大小的时候需要保证设置的值是操作系统"数据块"4KB的整数倍,从而保证通过操作系统和磁盘交互时"数据块"的完整性,不被分割或浪费,所以规定了innodb_page_size可以设置的值,分别是4096、8192、16384、3276865536,对应4KB、8KB、16KB、32KB、64KB。
解答问题
可以通过系统变量
innodb_page_size
进行调整与查看,但要保证设置的值是操作系统"数据块"4KB
的整数倍,MySQL规定innodb_page_size
可以设置的值,分别是 40968192、16384、65536,对应4KB、32768、8KB、16KB、32KB、64KB
🏳️🌈二、页都有哪些分类? 我们需要重点学习哪种页?
- InnoDB在不同的使用场景定义多种不同类型的页,常用的有数据页、
Undo Log页
、Change Buffer页
、Extent Descriptor(XDES)页
、InnoDB段信息页
等,每种页的数据结构都不相同,其中最需要我们关注的就是数据页 - 由于 InnoDB 中有个概念叫 “索引即数据”,所以也叫做索引页。
- 不论哪种类型的页都具有
页头(File Header)
和页尾(File Trailer)
两个信息
🏳️🌈三、页头和页尾具体包含了哪些信息?
页头 和 页尾 中包含的是 用来描述文件相关的信息
,如下图所示
3.1 页头 - File Header
- 页号:
FIL_PAGE_OFFSET
占用4Byte
,相当于页的身份证号,通过这个长度可以计算出每个InnoDB表中最多可以拥有 2(4*8)-1约42亿 个页,表空间第一个页编号从0开始,之后的页号分别是1,2,3…依此类推,具体页的偏移量计算公式为:页号 * 每页大小
; 那么按照每个页默认16KB大小计算,一个表空间最大容量为2(4*8)*16KB= 64TB,这也是InnoDB表空间最大容量是64T的原因; - 上一页页号:
FIL_PAGE PREV
- 下一页页号:
FIL_PAGE_NEXT
多个页通过这两个信息组成双向链表,即使不同的页地址不连续,也可以通过链表连接 - 表空间ID:
FIL PAGE_ARCH_LOG_NO_OR_SPACE_ID
,当前页属于哪个表空间 - 页类型:
FIL_PAGE_TYPE
,数据页对应的页类型是 FIL_PAGEINDEX=0x45BF - 最近一次修改的LSN:
FIL_PAGE_LSN
,占用8Byte
- 已被刷到磁盘的LSN:
FIL_PAGE_FILE_FLUSH_LSN
,占用8Byte
- 校验和:
FILPAGELSPACE_OR_CHKSUM
,用于页的完整性校验
3.2 页尾-File Trailer
- 近一次修改的LSN
- 校验和: 对应页头中的校验和
如果在数据传输的过程中数据丢失或异常中断,导致一个数据页不完整 就可以通过 页头
和 页尾
的 校验和
进行验证,验证算法默认使用 CRC32
3.3 什么是 LSN
LSN: 是"Log Sequence Number
"的缩写,表示日志序号。用 一个任意的、不断增加的值
表示日志中记录的操作对应的时间点,用 8字节的无符号长整形
表示,后面会详细介绍如何生成LSN的值
3.4 除了页头和页尾,数据页中还有哪些信息?
页头和页尾中的各个字段描述了当前页的类型以及在文件系统中的位置,也就是说通过页头可以找到对应的页。数据页的主要功能是保存数据,在一个数据页中,除了页头与页尾占用的46个字节之外的空间都用来存储真正的数据,也就是 数据行
数据行会与表里的数据行一一对应,基于这一特性MySQL也被称为"行式数据库”,也可以把除了页头页尾的区域称为页主体。
3.5 页主体中包含哪些信息?
页主体中的信息都是和数据相关的,
- 其中包括刚才提到了
数据行
- 还有为了提高查询效率的页目录
Page Directory
- 为了方便操作和管理数据页的数据页头
Page Header
这又是三个非常重要的概念,接下来我们逐个讨论。
🏳️🌈四、数据行有哪些信息组成?
数据行主要存储真实数据,为了方便数据的管理与描述,InnoDB在每个数据行中还添加了一些额外(管理)信息
,于是每一个 DYNAMIC
数据行都可以划分为两部分,一部分存储额外信息,一部分存储真实数据
额外信息部分包含 变长字段长度列表
和 NULL值列表
两个大小不确定的区域,以及固定占5字节及40BIT的头信息区域
头信息 中存储了行的基本信息,包括行在 页内的位置heap_no
、行类型 record_type
、下一行的地址偏移量 next_record
等6项信息,
如下图所示:
总结
- 数据行可以划分为两部分,一部分存储
额外信息
,一部分存储真实数据
- 额外信息部分 包含
变长字段长度列表
和NULL值列表
两个大小不确定的区域,以及固定占5字节的头信息区域
4.1 数据行是如何组织在一起的?
数据行通过下一行的地址偏移量,即 next_record
将页内所有数据行组成了一个单向链表
这里要注意的是,地址偏移量
指向的是 下一行中真实数据的起始地址
,这样做的好处是,向右是真实数据,向左就是头信息,而无需额外的长度计算,如图所示:
4.2 怎么标识新页中的第一行和最后一行?
了解了行的基本结构和组织方式之后,那么当遍历页中的行时,从哪里开始到哪里结束呢?
为了解决这个问题,每当创建一个新页,都会自动分配两个行,
- 一个是行类型为2的
最小行
Infimun, heap_no 位置固定为0号 - 一个是行类型为3的
最大行
Supremun, heap_no 位置固定为1号
这两个行并不存储任何真实信息,而是做为数据行链表的头和尾
虽然不存储真实数据,但它们的数据结构和真实数据行完全一致,只不过数据区域存储的是代表它们身份的固定字符串 Infimun
和 Supremun
,新页中没有数据时,最小行 Infimun
的 next record
直接连接最大行 Supremun
,最大行不连接任何行,它的 next_record
为 0
4.3 当向一个新页插入数据时是如何执行的?
当向一个新页插入数据时,heap_no
会 从2号开始递增,表示当前记录在页面堆中的相对位置;
- 如果是真实数据则
record_type
为0
- 如果是索引目录(B+树非叶节点)数据则
record_type
为1
再将 Infimun
连接第一个数据行,最后一行真实数据行连接 Supremun
,这样数据行就构建成了一个单向链表
更多的行数据插入后,会按照主键从小到大
的顺序进行链接;
为了使页的结构更加清晰,通常
- 将页中有数据行的区域称为
用户数据区 userRecords
- 把未被数据行占用的区域称为
空闲区Free Spac
e
如下图所示:
🏳️🌈五、如果要查询的数据在某一个页中,如何定位它在页中的位置,一条条遍历吗?
当然不是,InnoDB使用了另一种方式,更高效的查询数据,下面我们分析一下。
5.1 一条条遍历的查询效率高不高?
从头开始遍历是一个最简单的方法,也可以实现数据的查找,当按主键或索引查找某条数据时,从头行 infimun
开始,沿着链表顺序逐个比对查找,但一个页有16KB
,通常会存在数百行数据
,每次都要遍历数百行,无法满足高效查询。
5.2 如何提高页内的查询效率?页目录
为了提高查询效率,InnoDB
采用 二分查找
来解决查询效率问题。
具体实现方式是,在每一个页中加入一个叫做 页目录 Page Directory
的结构,将页内包括头行、尾行在内的所有行进行分组,约定头行单独为一组
,其他每个组最多8条数据
,同时把每个组最后一行在页中的地址,按主键从小到大的顺序记录在页目录中
页目录中的每一个位置称为一个槽
,每个槽都对应了一个分组
,这样在插入数据行完成链接后,一旦最后一个分组中的数据行超过分组的上限8个时,就会分裂出一个新的分组,为了快速判断每个分组是否达到了8个的上限,在每个分组最后一行中用 n_owned 记录了这个分组内的行数,与此同时在页目录中创建一个新的槽,后续插入的行都遵守这个规则;
后续在查询某行时,就可以通过二分查找
,先找到对应的槽
,然后在槽内最多8个数据行中进行遍
历即可,从而大幅提高了查询效率;
例如要查找主键为6的行,先比对槽中记录的主键值,定位到最后一个槽2,再从最后一个槽中的第一条记录遍历,第二条记录就是我们要查询的目标行。
🏳️🌈六、关于事务、索引这些信息在页中怎么记录?
🏳️🌈七、数据页的完整结构是什么样的?
这个问题是对页结构的总结性描述,这里也用一张图就可以明确的表示出页结构的整体信息
注意: 这里讲的是 InnoDB
的数据页结构,和 MyISAM
的页结构有所不同
👥总结
本篇博文对 【MySQL】页结构详解:页的大小、分类、头尾信息、数据行、查询、记录及数据页的完整结构 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~