目录
1、对数据页中的每一行数据,他在磁盘上是怎么存储的?
CREATE TABLE table_name (columns) ROW_FORMAT=COMPACTALTER TABLE table_name ROW_FORMAT=COMPACT
变长字段的长度列表,null值列表,数据头,column01的值,column02的值,column0n的值......
2、VARCHAR这种变长字段,在磁盘上到底是如何存储的
2.1、变长字段存储:
举例:对于“hello a a "和“hi a a" 两行字段,其几个字段的类型为VRACHAR(10),CHAR(1),CHAR(1)。而hello长度为5,则16进制为0x05,hi长度为2,则16进制为0x02
则存储如下:
0x05 null值列表 数据头 hello a a 0x02 null值列表 数据头 hi a a
2.2、变长字段读取:
读取“hello a a”这行数据
- 先知道这个表里的三个字段的类型是VARCHAR(10) CHAR(1) CHAR(1)
- 然后读取变长字段的长度列表,里面会读取到一个0x05这个十六进制的数字,发现第一个变长字段的长度是5
- 然后后续两个字段都是CHAR(1),长度都是固定的1个字符,于是此时就依次按照长度为1读取出来后续两个字段的值,分别是“a”“a”。
2.3、多个变长字段,如何存放他们的长度?
hello hi hao a a
0x03 0x02 0x05 null值列表 头字段 hello hi hao a a
2.4、一行数据中的多个NULL字段值在磁盘上怎么存储?
null值列表:你一行数据里可能有的字段值是NULL,比如你有一个name字段,他是允许为NULL的,那么实际上在存储的时候,如果你没给他赋值,他这个字段的值就是NULL。
NULL值是以二进制bit位来存储的。一行数据里的NULL值是肯定不会直接按照字符串的方式存放在磁盘上浪费空间的。
0x09 0x04 00000101 头信息 column1=value1 column2=value2 ... columnN=valueN
2.5、数据头
- 第1个bit位和第2个bit位,都是预留位,是没任何含义的。
- 第3个bit位是delete_mask,他标识这行数据是否被删除了,这么说在MySQL里删除一行数据的时候,未必是立马把他从磁盘上清理掉,而是给他在数据头里搞1个bit标记他已经被删。
- 第4个bit位是min_rec_mask,是在B+树里每一层的非叶子节点里的最小值都有这个标记。
- 第5-8个bit位是n_owned,就是记录了一个记录数,这个记录数的作用。
- 第9-21个bit位是heap_no,他代表的是当前这行数据在记录堆里的位置。
- 第22-25个bit位是record_type,这就是说这行数据的类型,0 代表的是普通类型,1代表的是B+树非叶子节点,2代表的是最小值数据,3代表的是最大值数据
- 第26-40个bit的next_record,这个是指向他下一条数据的指针。
3、每一行的实际数据在磁盘上是的存储
CREATE TABLE customer (
name VARCHAR(10) NOT NULL,
address VARCHAR(20),
gender CHAR(1),
job VARCHAR(30),
school VARCHAR(50)
) ROW_FORMAT=COMPACT;
在磁盘上是怎么来存储的:“jack NULL m NULL xx_school”?
实际一行数据可能看起来如下所示:
0x09 0x04 00000101 0000000000000000000010000000000000011001 00000000094C(DB_ROW_ID) 00000000032D(DB_TRX_ID) EA000010078E(DB_ROL_PTR) 616161 636320 6262626262
存储:刚开始先是他的变长字段的长度,用十六进制来存储,然后是NULL值列表,指出了谁是NULL,接着是40个bit位的数据头, 然后是隐藏字段,最后是真实的数据值就放在后面。
读取:首先他必然要把变长字段长度列表和NULL值列表读取出来,通过综合分析一下,就知道有几个变长字段,哪几个变长字段是NULL,因为NULL值列表里谁是NULL谁不是NULL都一清二楚。
此时就可以从变长字段长度列表中解析出来不为NULL的变长字段的值长度,然后也知道哪几个字段是NULL的,此时 根据这些信息,就可以从实际的列值存储区域里,把你每个字段的值读取出来了。
如果是变长字段的值,就按照他的值长度来读取,如果是NULL,就知道他是个NULL,没有值存储,如果是定长字 段,就按照定长长度来读取,这样就可以完美的把你一行数据的值都读取出来了!
- 根据变长字段的长度,先读取出jack这个值,因为他长度是4,就读取4个长度的数据jack ;
- 然后发现第二个字段是NULL,就不用读取了;
- 第三个字段是定长字段,直接读取1个字符就可以了,就是m这个值;
- 第四个字段是NULL,不用读取了;
- 第五个字段是变长字段长度是9,读取出来xx_school就可以了。
4、行溢出
万一 一行数据的大小超过了页的大小怎么办呢?
行溢出,就是说一行数据存储的内容太多了,一个数据页都放不下了,此时只能溢出这个数据页,把数据溢出存放到其他数据页里去,那些数据页就叫做溢出页。 包括其他的一些字段类型都是一样的,比如TEXT、BLOB这种类型的字段,都有可能出现溢出,然后一行数据就会存储在多个 数据页里。
5、数据页
数据页表示
每个数据页,实际上是默认有16kb的大小,那么这16kb的大小就是存放大量的数据行吗?
其实一个数据页拆分成了很多个部分,大体上来说包含了文件头、数据页头、最小记录和最大记录、多个数据行、空闲空间、数据页目录、文件尾部。
其中文件头占据了38个字节,数据页头占据了56个字节,最大记录和最小记录占据了26个字节,数据行区域的大小是不固定的,空闲区域的大小也是不固定的,数据页目录的大小也是不固定的,然后文件尾部占据8个字节。
通过这种特殊的存储格式在磁盘文件里去存放一个又一个的数据页,每个数据页在磁盘里实际存储的时候, 就是包含了上述一些特殊的数据,然后每个数据页里还有专门的区域包含了多个数据行,至于每个数据行,那就是用我们之前讲解的那套存储格式来存储的了。
数据插入数据页过程
1)、刚开始一个数据页可能是空的,没有一行数据,此时数据页实际上没有数据行那个区域。
2)、现在要插入一行数据,此时数据库里可是一行数据都没有的,那么先从磁盘上加载一个空的数据页到缓存页里去。
3)、在缓存页里插入一条数据,实际上就是在数据行那个区域里插入一行数据,然后空闲区域的空间会减少一 些。
4)、不停的插入数据到这个缓存页里去,直到他的空闲区域都耗尽了,就是这个页满了,此时数据行区域内可能有很多行数据,空闲区域就没了。
5)、在更新缓存页的同时,其实他在lru链表里的位置会不停的变动,而且肯定会在flush链表里,所以最终他一定会通过后台IO线程根据lru链表和flush链表,把这个脏的缓存页刷到磁盘上去。