页面结构
⻚在MySQL运⾏的过程中起到了⾮常重要的作⽤,为了能发挥更好的性能,可以结合⾃⼰系统的业务场景和数据⼤⼩,对⻚相关的系统变量进⾏调整,⻚的⼤⼩就是⼀个⾮常重要的调整项。同时关于⻚的结构也要有所了解,以后介绍的索引原理也是基于⻚实现的。⾸先来看关于⻚的⼏个问题。
1 ⻚的⼤⼩可以设置吗?
分析过程
- 前⾯介绍了每个数据⻚默认为 16KB ,是操作系统"数据块" 4KB的整数倍,那么只要保证⻚的⼤⼩是操作系统"数据块" ⼤⼩的整数倍是不是也可以呢,答案是肯定的。
- MySQL提供了⼀个专⻔的系统变量来控制⻚的⼤⼩,可以通过系统变量 innodb_page_size 进⾏调整与查看,在调整⻚⼤⼩的时候需要保证设置的值是操作系统"数据块" 4KB的整数倍,从⽽保证通过操作系统和磁盘交互时"数据块"的完整性,不被分割或浪费,所以规定了innodb_page_size 可以设置的值,分别是 4096 、 8192 、 16384 、 32768 、65536 ,对应 4KB 、 8KB 、 16KB 、 32KB 、 64KB 。
解答问题
- 可以通过系统变量 innodb_page_size 进⾏调整与查看,但要保证设置的值是操作系统"数据块" 4KB的整数倍,MySQL规定 innodb_page_size 可以设置的值,分别是 4096 、8192 、 16384 、 32768 、 65536 ,对应 4KB 、 8KB 、 16KB 、 32KB 、 64KB 。
2 ⻚都有哪些分类?我们需要重点学习哪种⻚?
- InnoDB在不同的使⽤场景定义多种不同类型的⻚,常⽤的有 数据⻚ 、 Undo Log⻚ 、Change Buffer⻚ 、 Extent Descriptor(XDES)⻚ 、 InnoDB段信息⻚ 等,每种⻚的数据结构都不相同,其中最需要我们关注的就是数据⻚,由于InnoDB中有个概念叫 “索引即数据”,所以也叫做索引⻚。
- 不论哪种类型的⻚都具有⻚头(File Header)和⻚尾(File Trailer)两个信息
3 ⻚头和⻚尾具体包含了哪些信息?
解答问题
⻚头和⻚尾中包含的是⽤来描述⽂件相关的信息,如下图所⽰:
- ⻚头 - File Header
- ⻚号: FIL_PAGE_OFFSET 占⽤ 4Byte ,相当于⻚的⾝份证号,通过这个⻓度可以计算出每个InnoDB表中最多可以拥有 2^(48)-1约42亿 个⻚,表空间第⼀个⻚编号从0开始,之后的⻚号分别是1,2,3…依此类推,具体⻚的偏移量计算公式为:⻚号 * 每⻚⼤⼩;那么按照每个⻚默认16KB⼤⼩计算,⼀个表空间最⼤容量为 2^(48) * 16KB = 64TB ,这也是InnoDB表空间最⼤容量是64T的原因;
- 上⼀⻚⻚号: FIL_PAGE_PREV
- 下⼀⻚⻚号: FIL_PAGE_NEXT 多个⻚通过这两个信息组成双向链表,即使不同的⻚地址不连续,也可以通过链表连接
- 表空间ID: FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID ,当前⻚属于哪个表空间
- ⻚类型: FIL_PAGE_TYPE ,数据⻚对应的⻚类型是 FIL_PAGE_INDEX = 0x45BF
- 最近⼀次修改的LSN: FIL_PAGE_LSN ,占⽤8Byte
- 已被刷到磁盘的LSN: FIL_PAGE_FILE_FLUSH_LSN ,占⽤8Byte
- 校验和: FIL_PAGE_SPACE_OR_CHKSUM ,⽤于⻚的完整性校验
- ⻚尾 - File Trailer
-
最近⼀次修改的LSN
-
校验和:对应⻚头中的校验和
如果在数据传输的过程中数据丢失或异常中断,导致⼀个数据⻚不完整就可以通过⻚头和⻚尾的校验和进⾏验证,验证算法默认使⽤ CRC32
3.1 什么是LSN?
LSN:是"Log Sequence Number"的缩写,表⽰⽇志序号。⽤⼀个任意的、不断增加的值表⽰⽇志中记录的操作对应的时间点,⽤8字节的⽆符号⻓整形表⽰,后⾯会详细介绍如何⽣成LSN的值
3.2 除了⻚头和⻚尾,数据⻚中还有哪些信息?
- ⻚头和⻚尾中的各个字段描述了当前⻚的类型以及在⽂件系统中的位置,也就是说通过⻚头可以找到对应的⻚。数据⻚的主要功能是保存数据,在⼀个数据⻚中,除了⻚头与⻚尾占⽤的46个字节之外的空间都⽤来存储真正的数据,也就是数据⾏,数据⾏会与表⾥的数据⾏⼀⼀对应,基于这⼀特性MySQL也被称为 “⾏式数据库” ,也可以把除了⻚头⻚尾的区域称为⻚主体。
3.3 ⻚主体中包含哪些信息?
- ⻚主体中的信息都是和数据相关的,其中包括刚才提到了数据⾏,还有为了提⾼查询效率的⻚⽬录 Page Directory 和为了⽅便操作和管理数据⻚的数据⻚头 Page Header ,这⼜是三个⾮常重要的概念,接下来我们逐个讨论。
4 数据⾏有哪些信息组成?
分析过程
- 数据⾏主要存储真实数据,为了⽅便数据的管理与描述,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 Space ,如下图所⽰:
5 如果要查询的数据在某⼀个⻚中,如何定位它在⻚中的位置,⼀条条遍历吗?
分析过程
- 当然不是,InnoDB使⽤了另⼀种⽅式,更⾼效的查询数据,下⾯我们分析⼀下。
5.1 ⼀条条遍历的查询效率⾼不⾼?
- 从头开始遍历是⼀个最简单的⽅法,也可以实现数据的查找,当按主键或索引查找某条数据时,从头⾏ infimun 开始,沿着链表顺序逐个⽐对查找,但⼀个⻚有16KB,通常会存在数百⾏数据,每次都要遍历数百⾏,⽆法满⾜⾼效查询。
5.2 如何提⾼⻚内的查询效率?⻚⽬录
- 为了提⾼查询效率,InnoDB采⽤⼆分查找来解决查询效率问题。
- 具体实现⽅式是,在每⼀个⻚中加⼊⼀个叫做⻚⽬录 Page Directory 的结构,将⻚内包括头⾏、尾⾏在内的所有⾏进⾏分组,约定头⾏单独为⼀组,其他每个组最多8条数据,同时把每个组最后⼀⾏在⻚中的地址,按主键从⼩到⼤的顺序记录在⻚⽬录中在,⻚⽬录中的每⼀个位置称为⼀个槽,每个槽都对应了⼀个分组,这样在插⼊数据⾏完成链接后,⼀旦最后⼀个分组中的数据⾏超过分组的上限8个时,就会分裂出⼀个新的分组,为了快速判断每个分组是否达到了8个的上限,在每个分组最后⼀⾏中⽤ n_owned 记录了这个分组内的⾏数,与此同时在⻚⽬录中创建⼀个新的槽,后续插⼊的⾏都遵守这个规则;
- 后续在查询某⾏时,就可以通过⼆分查找,先找到对应的槽,然后在槽内最多8个数据⾏中进⾏遍历即可,从⽽⼤幅提⾼了查询效率;
- 例如要查找主键为6的⾏,先⽐对槽中记录的主键值,定位到最后⼀个槽2,再从最后⼀个槽中的第⼀条记录遍历,第⼆条记录就是我们要查询的⽬标⾏。
- 为了提⾼查询效率,在每⼀个⻚中加⼊⼀个叫做⻚⽬录 Page Directory 的结构,采⽤⼆分查找来解决查询效率问题。
6 关于事务、索引这些信息在⻚中怎么记录?
解答问题
- ⼀张图就可以明确的表⽰出这些信息在⻚中是怎么记录的,如下图所⽰:
7 数据⻚的完整结构是什么样的?
🔍 分析过程
-
这个问题是对⻚结构的总结性描述,这⾥也⽤⼀张图就可以明确的表⽰出⻚结构的整体信息
-
注意:这⾥讲的是InnoDB的数据⻚结构,和MyISAM的⻚结构有所不同
✅ 解答问题
- 下图是数据⻚的完整结构,以及所占的磁盘空间
❤ ⼩结
⾄此⼀个⻚的核⼼结构就介绍完了,主要内容包括:
设置⻚的⼤⼩
⻚的主要分类
⻚头和⻚尾包含的信息
⻚主体的组成部分
数据⾏的组成部分
数据⻚头包含的统计和描述信息