MySql之我们写入一行数据,在磁盘上是怎么存储的

1,299 阅读7分钟

1:承上启下:在Buffer Pool之后,为什么要学习MySql数据存储模型?

  • 通过上一篇文章juejin.cn/post/694455… 我们知道当我们执行crud的时候,MySql是如何将数据加载到Buffer Pool中并进行后续的操作的。并且包括free,flush,lru等链表的作用,还包括缓存刷盘等机制

2:MySql为什么要引入数据页的概念?

  • 当我们要更新一条数据的时候,首先会从磁盘中找到这条数据,然后加载到Buffer Pool缓冲区中,如果我们要更新多条数据,难道还要一条一条的从磁盘中国读取?? 这样效率高嘛??
  • 所以InnoDb引入了数据页的概念,也就是把数据组织成一页一页的概念,每个页是16kb,每次加载至少是加载一个页的数据,甚至是多页的数据,这样就不用一条一条的去磁盘加载了

3:初始MySql物理数据存储格式,一行数据在磁盘上是如何存储的?

  • 当我们存储一条数据到MySql中去的时候,每一条数据的存储格式大致是下面这样的
    • 变长字段的长度列表 , null值列表 , 数据头 , colimn01的值 , column02的值 ... column0n的值
  • 也就是除了整行的数据之外,还包含了 变长字段的长度列表 , null值列表 , 数据头一些额外信息

4:对于VARCHAR这种边长字段,在磁盘上到底是如何存储的?

  • 大家都知道,MySql是有一些字段的长度是可变的,比如VARCHAR,假设我们现在有一行数据,VARCHAR(10),char(1),char(1),第一个字段的长度是可变的,现在有一行数据 hello a a,然后还有一行数据是 hell a a,那么这二行数据在同一个磁盘里的存储格式是这样的 hello a a hell a a,没错,二行数据是挨着的,其实平时表里很多数据在磁盘中都是这样挨着存储的

  • 当我们要读取一行数据怎么办??

    • 如果我们现在要读取 hello a a 这行数据是非常困难的,因为第一个字段的长度是可变的,所以我们不知道我们要读取的这行数据到底有多长,也就很难将一整行完整的数据取出来
  • 引入变长字段列表,解决一行数据难读取的问题

    • 所以我们需要在保存这条数据信息的时候,同时保存变长字段的长度列表,这样才能解决一行数据的读取问题。也就是说才存储 hello a a,这行数据的时候,需要带上一些额外的信息,我们看到hello的长度是5,十六机制是0x05,所以在存储hello a a这行数据的时候会存储一些额外的信息,其实类似如下这种数据格式:0x05 null值列表 数据头 hello a a ,
  • 多行数据存储格式

    • 如果现在有多行数据,比如有 hello a a,hi a a,这二行数据,那么存储格式类似如下:0x05 null值列表 数据头 hello a a 0x02 null值列表 数据头 hi a a
  • 引入变长字段列表,如何读取变长字符?

    • 如果我们要读取一行数据,会判断一下该字段是不是变长字段,如果是就会从变长字段列表中拿到这个字段的长度,比如 0x05,代表该字段的长度是5,那么 hello a a,就能够拿到hello这个字段的值了
  • 如果有多个变长字段怎么办?

    • 假设我们现在有有一个表是这样的 VARCHAR(15),VARCHAR(20),CHAR(10),也就是有多个变长字段,那么在存储一行数据的时候肯定就要存储多个变长字段了,假设现在我们存储一行数据是 hello hi abc,因为第一个,第二个字段是变长字段,hello hi的长度分别是0x05 0x02,所以变长的字段列表是 0x05 0x02,但是MySql真正存储的时候是逆序的,所以整个数据格式如下:0x02 0x05 null值列表 数据头 hello hi abc

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

  • 为什么一行数据的NULL值不能直接存储
    • 如果现在我们有一个字段为空,如果直接存储一个NULL值的话,那么有什么意义呢?只会浪费我们的存储空间
  • NULL值以二进制bit来存储
    • 如果一行数据由多个NULL值,那么对所有的NULL值不会直接在磁盘上存储,而是通过二进制的把bit来存储,假设我们现在有一张表,表有: id name address age几个字段,注意除了主键id不能为空,其它几列都是可以为空的,比如我们现在有一行数据是:1 NULL hello NULL,其中 name和age都是为NULL的,所以这行数据的bit位就是 101,解释一下:这个表有4个字段,本来实际的格式是这样的:0101,0表示不为NULL,1表示为NULL,但是因为NULL值列表值只会存储可以为NULL的字段,主键ID是不能为NULL的,所以只会存储name address age三个字段,所以是 101,但是MySql中存储NULL值列表是逆序的,所以最终结果是 101,所以整行数据的存储格式如下:0x05 101 数据头 column1=value1 column2=value2 ...,实际上在存储NULL值列表的时候,都是8bit的倍数,如果不满足的话就高位补0,所以实际存储的格式是这样的:0x05 00000101 数据头 column1=value1 column2=value2 ...

6:理解数据在磁盘上的物理存储之后,聊聊行溢出是什么东西?

  • 我们存储的数据都是以数据页的形式在MySql中存储起来的,每个数据页的大小是16kb,那么如果一行的数据大于16kb怎么办??如果一行数据在一个数据页放不下,那么溢出的数据就会存放到下一个数据页中,这就是行溢出。当然如果要读取这行数据也会把存放这行数据的多个数据页一起加载到Buffer Pool中

7:用于存放磁盘上的多行数据的数据页到底长个什么样子?

  • 现在我们知道我们一行行的数据都是存放在数据页中的,当我们刚开始初始化一个数据库的时候,我们知道这个数据库中的数据肯定是没有的,那么数据页的整体格式如下图 41617603030_.pic.jpg
  • 那么现在我们需要插入一条数据,那么格式就是这样的了

21617602877_.pic_hd.jpg 如果此时插入了多行数据,格式就是下面这样的:

31617602890_.pic_hd.jpg

  • 此时你会发现,如果空闲区域没有了,那么这个数据页也就满

8:表空间以及划分多个数据页的数据区,又是什么概念?

  • 表空间: 简单来说,我们平时创建的表都有表空间的概念,在磁盘上都会对应着一个“表名.ibd”这样一个数据文件,所以其实在物理层面,表空间就是对应这磁盘上的一个个的数据文件
  • 数据区:我们的数据都是存储在数据页中的,一个表空间可能会有多个数据页,如果一个表空间中的数据页很多就不方便管理,所以就引入了一个数据区的概念,一个数据区对应的连续的64的数据页,