本文共 4953 字,大约阅读时间需要 16 分钟。
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称ALTER TABLE 表名 ROW_FORMAT=行格式名称
上一节学习的是行格式,我们每次插入一个记录就是一行,在数据库中存储的格式。
接下来学习页格式,针对不同类型的数据,会存储在不同的页中,比如专门存放数据的叫数据页,也叫索引页。(改:针对不同目的,设计了不同类型的页。)
InnoDB 数据页主要由以下7个存储空间组成:
(1)File Header 文件头部 (2)Page Header 页头部 (3)Free Space 空白空间 (4)User Recorder 用户记录 (5)最小和最大记录 (6)? (7)?(补: InnoDB 数据页的存储空间分为7个部分: (1)File Header 文件头部 38字节 页的通用信息 (2)Page Header 页面头部 56字节 数据页转有的一些信息 (3)Infimum Supremum 最小记录和最大记录 26字节 2个虚拟的行记录 (4)User Records 用户记录 不确定 实际存储的行记录内容 (5)Free Space 空闲空间 不确定 页中尚未使用的空间 (6)Page Directory 页面记录 不确定 页中某些记录的相对位置 (7)File Trailer 文件尾部 8字节 校验是否完整 )
要插入记录时,会在Free Space(空闲空间)中申请一块区域(一条记录的大小),将空间划给User Recorder ,并将记录存入。不是按照插入的顺序的,是依据主键大小,从小到大形成单链表,之前行格式中有“记录头信息”(真实的信息那部分),在“记录头信息”中有以下属性:
next ???、delete_???、type???(改: next_record:从当前记录的真实数据到下一条记录的真实数据的地址偏移量。 delete_mask 补: record_type:当前记录的类型:0-普通记录,1-B+树非叶节点记录,2-最小记录,3-最大记录。 min_rec_mask:B+树的每层非叶子节点的最小记录标记。 heap_no:当前记录在本页中的位置 会自动给每个页加两个记录:(因为这两个记录不是我们自己加的,所以叫“伪记录”或“虚拟记录”) 最小 Infimum,最大 Supremum 都是5字节的记录头信息和8字节大小的固定部分组成的: )
就相当于数据结构的链表中的next域,会指向比它的主键值大的下一条记录。形成的单链表会从最小记录–> a --> b…–>最大记录。
如果要删除某条记录,不会删掉该节点,而是在delete_???那个属性做标记,置为1则是删除,0是未删除,然后把删除的记录形成删除链表,那些空间是可以复用的。(改:delete_mask)
在整个页中,还有“槽”的概念,记录是分组的,分组的规则是这样的:
最小记录和最大记录先分别占个组,这样先有两个组了。 接下来按照主键的大小,从小到大,会把记录插在比它大而且相差最小的那个组里,每个组中最大的记录是“槽”,相当于“槽”指向它自己表示的那个组的主键最大的记录,每组最多有8个记录。把我理解的“槽”画了出来( 灵 魂画手在线画图 ):(槽到底是个神马?一条条记录吗?槽是否有编号?槽指向的还需要指向下一个大的记录吗?)
(答:槽是每个组里主键值最大的那条记录的地址偏移量,会被单独提取出来按顺序放在页的 Page Directory页目录中,页目录里都是槽。 槽没有编号,被按从小到大的顺序放置。 需要,不影响。)
执行查找的SQL语句时,就是先通过槽定位到组,然后 ???
(见 三)
Page Header用于记录当前页的信息,比如页的类型?(索引页、B+数每层非叶子页 ??? )页中有多少条记录?有多少条被删除的记录?
(改: 在页目录中的槽数量, 还未使用的空间最小地址, 本页中的记录的数量(包括最小和最大记录以及标记为删除的记录), 第一个已经标记为删除的记录地址, 已删除记录占用的字节数, 最后插入记录的位置, 记录插入的方向, 一个方向连续插入的记录数量, 该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录), 修改当前页的最大事务ID(该值仅在二级索引中定义,当前页在B+树中所处的层级), 索引ID(表示当前页属于哪个索引), B+树叶子段的头部信息(仅在B+树的Root页定义), B+树非叶子段的头部信息(仅在B+树的Root页定义) )
File Header用于记录文件所有页的类型?多少页? ???
(改: FIL_PAGE_SPACE_OR_CHKSUM 页的校验和(checksum值), FIL_PAGE_OFFSET 页号,每一个页都有一个单独的页号,可以通过页号单独定位某一页 , FIL_PAGE_PREV 上一个页的页号, FIL_PAGE_NEXT 下一个页的页号, FIL_PAGE_LSN 页面被最后修改时对应的日志序列位置(英文名是:Log Sequence Number), FIL_PAGE_TYPE 该页的类型:最新分配,还没使用; Undo日志页; 段信息节点; Insert Buffer空闲列表; Insert Buffer位图; 系统页 ;事务系统数据 ;表空间头部信息 ;扩展描述页 ;溢出页 ;索引页/数据页 FIL_PAGE_FILE_FLUSH_LSN 仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值, FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 页属于哪个表空间。 )
记录与槽:
关于==“槽”==:
1.将所有正常的记录(包括最大和最小记录,不包括标记为已删除的记录)划分为几个组。 2.每个组的最后一条记录(也就是组内主键值最大的那条)的头信息中的n_owned
属性表示该组内共有多少条记录。 3.把 每个组的最后一条记录的地址偏移量(“槽”)单独提取出来,按顺序存储到靠近页尾部的部分,这个地方就是 Page Directory(页面目录)。 Page Directory 页面目录就是由槽组成的。 规定: 对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能在是 4~8 条之间。 分组规则: (1)初始数据页里只有最小记录和最大记录,分属两个分组。 (2)之后每插入一条记录,都从页目录中找到主键值比本记录大且差值最小的槽,然后把该槽对应的 n_owned
值+1,表示本组内又添加了一条记录,直到该组记录数=8 。 (3)在一个组中的记录数等于8个后,再插入一条记录时,会将组中的记录拆分成两个组,一个组中4条记录,另一个5条记录。这个过程会在页目录中新增一个槽来记录这个新增分组中最大的那条记录的偏移量。 所以在一个数据页中查找指定主键值的记录的过程分为两步:
(1)通过二分法确定该记录所在的槽,并找到该槽所在分组中主键值最小的那条记录。
(2)通过记录的next_record属性遍历该槽所在的组中的各个记录。PAGE_DIRECTION
假如新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边。用来表示最后一条记录插入方向的状态就是PAGE_DIRECTION。
PAGE_N_DIRECTION
假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记下来,这个条数就用PAGE_N_DIRECTION这个状态表示。当然,如果最后一条记录的插入方向改变了的话,这个状态的值会被清零重新统计。
FIL_PAGE_PREV
和FIL_PAGE_NEXT
、FIL_PAGE_PREV
和FIL_PAGE_NEXT
就分别代表本页的上一个和下一个页的页号。这样通过建立一个双向链表把许许多多的页就都串联起来了,而无需这些页在物理上真正连着。需要注意的是,并不是所有类型的页都有上一个和下一个页的属性,不过数据页(也就是类型为FIL_PAGE_INDEX的页)是有这两个属性的,所以所有的数据页其实是一个双链表。
(2)后4个字节代表页面被最后修改时对应的日志序列位置(LSN)
这个部分也是为了校验页的完整性的。LSN且听以后分解。File Trailer与File Header类似,都是所有类型的页通用的。
转载地址:http://ldjh.baihongyu.com/