Mysql基础(九):一行数据在磁盘上是如何存储的?

目录

1、对数据页中的每一行数据,他在磁盘上是怎么存储的?

2、VARCHAR这种变长字段,在磁盘上到底是如何存储的

2.1、变长字段存储:

2.2、变长字段读取:

2.3、多个变长字段,如何存放他们的长度?

2.4、一行数据中的多个NULL字段值在磁盘上怎么存储?

2.5、数据头

3、每一行的实际数据在磁盘上是的存储

4、行溢出

5、数据页

数据页表示

数据插入数据页过程 


1、对数据页中的每一行数据,他在磁盘上是怎么存储的?

行格式:我们可以对一个表指定行存储的格式,比如我们这里用一个 COMPACT格式
CREATE TABLE table_name (columns) ROW_FORMAT=COMPACT
ALTER 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、多个变长字段,如何存放他们的长度?

           比如一行数据有VARCHAR(10) VARCHAR(5) VARCHAR(20) CHAR(1) CHAR(1),一共5个字段,其中三个是变长字 段,此时假设一行数据是这样的:
hello hi hao a a
        此时在磁盘中存储的, 必须在他开头的变长字段长度列表中存储几个变长字段的长度,一定要注意一点,他这里是逆序存储的! 也就是说先存放VARCHAR(20)这个字段的长度,然后存放VARCHAR(5)这个字段的长度,最后存放VARCHAR(10)这 个字段的长度。
现在hello hi hao三个字段的长度分别是0x05 0x02 0x03,但是实际存放在变长字段长度列表的时候,是逆序放的,
0x03 0x02 0x05 null值列表 头字段 hello hi hao a a

2.4、一行数据中的多个NULL字段值在磁盘上怎么存储?

null值列表:你一行数据里可能有的字段值是NULL,比如你有一个name字段,他是允许为NULL的,那么实际上在存储的时候,如果你没给他赋值,他这个字段的值就是NULL。

NULL值是以二进制bit位来存储的。一行数据里的NULL值是肯定不会直接按照字符串的方式存放在磁盘上浪费空间的。

        你所有允许值为NULL的字段,注意,是允许值为NULL,不 是说一定值就是NULL了,只要是允许你为NULL的字段,在这里每个字段都有一个二进制bit位的值,如果 bit值是1说明是NULL,如果bit值是0说明不是NULL 。比如有4个字段都允许为NULL,每个人都会有一个bit位,这一行数据的值是“jack NULL m NULL xx_school”,
        然后其中2个字段是null,2个字段不是null,所以4个bit位应该是:1010
         NULL值列表存放的时候,不会说仅仅是4个bit位,他一般起码是8个bit位的倍数,如果不足8个bit位。就高位补0,所以实际存放看起来是如下的:
0x09 0x04 00000101 头信息 column1=value1 column2=value2 ... columnN=valueN

2.5、数据头

    有40个bit位的数据头,这个数据头是用来描述这行数据的。
  • 第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就可以了。
隐藏字段含义
 
        首先有一个DB_ROW_ID字段,这就是一个行的唯一标识,是他数据库内部给你搞的一个标识,不是你的主键ID字段。如果我 们没有指定主键和unique key唯一索引的时候,他就内部自动加一个ROW_ID作为主键。
        接着是一个DB_TRX_ID字段,跟事务相关,他是说这是哪个事务更新的数据,这是事务ID。
        最后是DB_ROLL_PTR字段,这是回滚指针,是用来进行事务回滚的。

4、行溢出

万一 一行数据的大小超过了页的大小怎么办呢?

        比如有一个表的字段类型是VARCHAR(65532),意思就是最大可以包含65532个字符,那也就是65532个字节,这就远大于16kb(16384)的大小了,也就是说这一行数据的这个字段都远超一个数据页的大小了!
        这个时候 实际上会在那一页里存储你这行数据,然后在那个字段中,仅仅包含他一部分数据,同时包含一个20个字节的指针,指向了其他的一些数据页,那些数据页用链表串联起来,存放这个VARCHAR(65532)超大字段里的数据

行溢出,就是说一行数据存储的内容太多了,一个数据页都放不下了,此时只能溢出这个数据页,把数据溢出存放到其他数据页里去,那些数据页就叫做溢出页。 包括其他的一些字段类型都是一样的,比如TEXT、BLOB这种类型的字段,都有可能出现溢出,然后一行数据就会存储在多个 数据页里。

5、数据页

数据页表示

每个数据页,实际上是默认有16kb的大小,那么这16kb的大小就是存放大量的数据行吗?

其实一个数据页拆分成了很多个部分,大体上来说包含了文件头、数据页头、最小记录和最大记录、多个数据行、空闲空间、数据页目录、文件尾部。

其中文件头占据了38个字节,数据页头占据了56个字节,最大记录和最小记录占据了26个字节,数据行区域的大小是不固定的,空闲区域的大小也是不固定的,数据页目录的大小也是不固定的,然后文件尾部占据8个字节。

        通过这种特殊的存储格式在磁盘文件里去存放一个又一个的数据页,每个数据页在磁盘里实际存储的时候, 就是包含了上述一些特殊的数据,然后每个数据页里还有专门的区域包含了多个数据行,至于每个数据行,那就是用我们之前讲解的那套存储格式来存储的了。

数据插入数据页过程 

1)、刚开始一个数据页可能是空的,没有一行数据,此时数据页实际上没有数据行那个区域。

2)、现在要插入一行数据,此时数据库里可是一行数据都没有的,那么先从磁盘上加载一个空的数据页到缓存页里去。

3)、在缓存页里插入一条数据,实际上就是在数据行那个区域里插入一行数据,然后空闲区域的空间会减少一 些

4)、不停的插入数据到这个缓存页里去,直到他的空闲区域都耗尽了,就是这个页满了,此时数据行区域内可能有很多行数据,空闲区域就没了。

5)、在更新缓存页的同时,其实他在lru链表里的位置会不停的变动,而且肯定会在flush链表里,所以最终他一定会通过后台IO线程根据lru链表和flush链表,把这个脏的缓存页刷到磁盘上去。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值