上一篇博客回顾:算法
1:数据库拥有众多的储存引擎,如今主要使用的是Inoodb,这个储存引擎有Compact,Redundant,Dynamic,Compressed四种行格式数据库
2:Compact行格式的结构分为变长数据长度列表,NULL值列表,记录头信息,真是数据储存学习
3:变长数据长度列表储存的是变长数据类型数据的字节数逆顺序,空值列不储存,NULL值列表储存非主键和没有被NOT NULL 修饰的列,二进制位逆顺序进行储存。大数据
4:记录头信息包括了偏移量,槽数量,本组数据量,是否被删除,数据类型,是否是B+树子节点等等信息。spa
5:真实数据会有三个三个虚拟列,ROW_ID(没有主键的时候自动生成),ROLL_POINTER,TRANSACTION_ID(事务管理ID)日志
5:行溢出数据的处理Compact行格式是使用最后记录下一页地址的方式,然而Redundant和Compressed是采用整页记录数据页地址的方式,后二者的Compressed采用压缩算法。code
6:对于char类似类型的数据来讲,若是咱们采用可变的字符集进行操做也是会在可变长度数据列表里面进行储存的。blog
7:对于可变数据长度列表储存的占用字节为1或者2,NULL值用二进制位,记录头信息在Compact行格式占用5个字节,Redundant占用6字节。一页空间16kb。索引
页储存结构:事务
咱们都知道的是数据库一个页的储存空间是16kb,那么这16kb的储存空间是怎么进行分配,数据在这个储存空间里是怎么样的一个格式呢?对于这些数据数据库都进行了什么操做?这就是咱们今天须要进行学习的内容。
可能按照顺序来讲的话不是太方便进行讲诉,并且看起来可能也不会效果太好,因此咱们一点一点根据功能的划分进行区分。
User Records:
这个区域对咱们插入的数据进行保存,须要说明的是本来这一块是不存在的,当咱们插入数据的时候这块区域才会被划分出来。而且是从Free Space进行的划分。当咱们的Free Space区域全部的空间都变成了UserRecords,那么这时候就是须要从新开辟一个储存页的时间了。
那么当咱们把数据放在这个区域里面的时候,是否是就是说没有一点规则,随便进行摆放,其实想想就知道了,当咱们数据过于庞大的时候,咱们随便摆放,查找会是多么的痛苦。因此接下来在咱们知道数据保存在那个位置之后咱们须要弄清楚的是数据在User Records里的状况。在这里咱们假设插入了四条咱们本身的记录:
两个虚拟的数据:咱们能够看到下图所示,咱们插入了四条记录的时候,可是在咱们这个页中存在的是六条记录,也就是两条咱们说的每一个页中都会存在的虚拟记录:最大记录和最小记录。他们都存infumum_supremum里面,由于不是咱们本身插入的记录因此是不在User_Record里面.最小记录默认在开始,最大记录在最末尾结束
接下来咱们再根据这六条记录描述一些问题:
1:咱们在每条数据的记录头里面提到的Record_Type标记的是这条数据的类型,当时咱们说的是有0普通数据,1叶子节点数据,2最小数据,3最大数据。咱们能够看到的是上边最大和 最小数据分别是3和2, 咱们本身插入数据的记录头信息在Record_type这里都是0.
2:咱们在记录头信息里面还能够看到的是delete_mask这个数据,表示的是数据是否被删除,0表示没有,1表示已经被删除,因此上边的数据都是0
3:heap_no咱们讲过是标记该数据在页中的位置,咱们能够看到插入数据分别是2,3,4,5。那么0和1去哪了,别着急,请看看最小记录和最大记录的该数据,是否是分别为0和1。我 们插入的数据都会从2开始计数,虚拟数据会占用默认的0和1的位置。
4:插入数据的排列是否就是数据插入的顺序,那显然是不可能的,你没想错,数据会根据大小进行排列,那么数据用什么进行大小的排列?显然就是主键进行比较。
5:next_record记录的就是相对于本条数据,下一条数据的地址偏移量,就是经过这条数据往下查找这么多字节就能够找到下一条数据,没错。他就是使用的链表进行连接的。以下图:
6:若是一条数据被删除,也就是它的delete_mask被标记为了1,那么这个会怎么进行改变?就是和链表一致,进行连接的切断就能够了。
7:咱们在进行数据查找的时候就是这么一条接一条的进行查找么?从最小记录开始根据next_record查找?那必然是耗时的一个活,显然是不可能的,因此在就出现了分组这个概念。
分组:
咱们能够看到的是六条数据分红了两组,首先是最小的虚拟数据独自一组,而后剩下的五个数据再分红一组。在这里须要知道的就是MySql数据库在每一个页中进行数据分组的时候默认的 最小数据是第一组,它拥有一条数据,就是最小数据,不能在插入其余数据。最大数据是第二组,咱们在进行数据插入的时候都是先插入最大数据组,当最大数据组知足的时候进行分裂,形 成普通的分组,而后再进来的数据又插入最大数据组,如此循环往复,完成数据的分组。
槽:
咱们还能够看到的是在分组的图里面出现了两个奇奇怪怪的东西,槽,咱们上一篇文章页诉说过这个玩意儿。每一个分组数据的相对于页的地址偏移量就是一个槽数据,一个分组有一个 槽,槽存在的位置就是页信息的Page Directory。在这里我须要强调的是在记录头信息中有个地址偏移量next_record,这个偏移量是本条数据相对于下一条数据位置,而后槽中的偏移量是分 组最后一条数据相对于页的偏移量。
寻找:
有了分组之后,咱们在进行数据查找的时候就是根据二分法肯定对应数据所在的槽位置,而后在使用记录头信息的next_record一条条进行查找。
n_owned:
这个数据咱们在记录头信息中一直看到,其实在这里就能够结束这个数据了。它表示的是该分组有多少条数据,存在于分组的最后一条信息中。咱们能够看到的是每一个分组的前面的数据 n_owned都是0,只有在最后一条数据上它才有值。
page_header
上边咱们经过数据的方式介绍了User_Records,infumum_supremum,page_directory,Free spce这四块空间的使用状况,接下来须要进行解释的就是page_header,file_header,file tailer这三块空间。
首先说的就是page_header,这个地方储存的就是数据的一些信息:
PAGE_N_DIR_SLOTS |
2 字节 |
在页目录中的槽数量 |
PAGE_HEAP_TOP |
2 字节 |
第一个记录的地址 |
PAGE_N_HEAP |
2 字节 |
本页中的记录的数量(包括最小和最大记录以及标记为删除的记录) |
PAGE_FREE |
2 字节 |
指向可重用空间的地址(就是标记为删除的记录地址) |
PAGE_GARBAGE |
2 字节 |
已删除的字节数,行记录结构中delete_flag 为1的记录大小总数 |
PAGE_LAST_INSERT |
2 字节 |
最后插入记录的位置 |
PAGE_DIRECTION |
2 字节 |
最后插入的方向 |
PAGE_N_DIRECTION |
2 字节 |
一个方向连续插入的记录数量 |
PAGE_N_RECS |
2 字节 |
该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录) |
PAGE_MAX_TRX_ID |
8 字节 |
修改当前页的最大事务ID,该值仅在二级索引中定义 |
PAGE_LEVEL |
2 字节 |
当前页在索引树中的位置,高度 |
PAGE_INDEX_ID |
8 字节 |
索引ID,表示当前页属于哪一个索引 |
PAGE_BTR |
10 字节 |
非叶节点所在段的segment header,仅在B+树的Root页定义 |
PAGE_LEVEL |
10 字节 |
B+树所在段的segment header,仅在B+树的Root页定义 |
在上边咱们须要说的就是PAGE_DIRECTION和PAGE_N_RECS这两个数据第一个指的是最后插入的方向,相对于上一条数据来讲,咱们新插入的比他大,就在右边,反之则在左边,这就是方 向。当咱们插入数据连续的都在右边或者是都在左边的时候就会记录下数量。固然若是改变方向的话这个数据会被清空从零开始计数。
File_Header:
上边讲的page_header就是对页储存记录的描述,那么这里的File_Header就是对页信息的描述:
名称 | 占用空间大小 | 描述 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM |
4 字节 |
页的校验和(checksum值) |
FIL_PAGE_OFFSET |
4 字节 |
页号 |
FIL_PAGE_PREV |
4 字节 |
上一个页的页号 |
FIL_PAGE_NEXT |
4 字节 |
下一个页的页号 |
FIL_PAGE_LSN |
8 字节 |
最后被修改的日志序列位置(英文名是:Log Sequence Number) |
FIL_PAGE_TYPE |
2 字节 |
该页的类型 |
FIL_PAGE_FILE_FLUSH_LSN |
8 字节 |
仅在系统表空间的一个页中定义,表明文件至少被更新到了该LSN值,独立表空间中都是0 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID |
4 字节 |
页属于哪一个表空间 |
咱们能够看到三个值,一个记录的是这个页的页号,上一个页,下一个页。针对上面咱们讲的一个页中的数据是采用单向链表的形式进行链接,那么咱们能够想到的是数据库中的页是采用的双向链表。
咱们在上面还能够看到的是 FIL_PAGE_TYPE这个值,描述的是这个页的类型,显然咱们数据库不可能就只有一种数据页,上面咱们讲的储存真实数据页就是数据页。FIL_PAGE_INDEX,也就是咱们提到的 B+树叶子节点。
名称 | 十六进制 | 描述 |
---|---|---|
FIL_PAGE_ALLOCATED |
0x0000 | 最新分配,还没使用 |
FIL_PAGE_UNDO_LOG |
0x0002 | Undo Log页 |
FIL_PAGE_INODE |
0x0003 | 段信息的节点 |
FIL_PAGE_IBUUF_FRE_LIST |
0x0004 | Insert Buffer空闲列表 |
FIL_PAGE_IBUF_BITMAP |
0x0005 | Insert Buffer位图 |
FIL_PAGE_TYPE_SYS |
0x0006 | 系统页 |
FIL_PAGE_TYPE_TRX_SYS |
0x0007 | 事务系统数据 |
FIL_PAGE_TYPE_FSP_HDR |
0x0008 | File Space Header |
FIL_PAGE_TYPE_XDES |
0x0009 | 扩展描述页 |
FIL_PAGE_TYPE_BLOB |
0x000A | BLOB页 |
FIL_PAGE_INDEX |
0x45BF | B+树的子节点 |
File Trailer
这玩意须要和上面File header中的FIL_PAGE_SPACE_OR_CHKSUM
这个属性系统校验和一块儿说。咱们都知道的是页是一块16kb的储存空间,不论是内存刷新到数据库取用都是一次操做16kb。那么若是在中途产生停电等不可抗拒的因素,这时候这里就起做用了。File Header位于页的开始,它会计算一个校验和,这个校验和你能够这么理解,当咱们须要一个很复杂的字符串的时候,每每会将它按照必定的算法进行计算出一个整数值,当和其它字符串进行比较的时候就用这个值。因此校验和也是这个道理,File Trailer是位于页尾部的,他也会储存一个校验和。若是数据不完整,那么两个校验和不可能一致,那么就能够断定这个数据页是损坏的。