暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL数据存储模型图解

菜涛学Java 2021-10-25
1760


一、在磁盘中如何存放一行数据

mysql表中一行数据的结构由以下几个部分组成:


  • 变长字段长度列表:如果要从磁盘读取第一行数据,首先要知道读取数据的长度,才能把数据在磁盘中给完整读取出来;这行数据有一个变长字段的类型为varchar(10),变长字段的长度是可能会变化的,导致一行数据的长度并不是固定的,mysql在设计每行数据结构时引入可变字段长度列表,对于每行数据,会把varchar类型字段的长度记录到变长字段长度列表中



    变长字段长度列表的大小是用16进制形式存放的,如果有多个字段为varchar类型,就会按照,以16进制的格式逆序存放到变长字段长度列表中

    读取数据时,扫描到第一个字段是变长字段类型varchar,就先到变长字段长度列表看下字段的长度,然后根据读取到的字段长度依次读取数据,然后接下来是char char都是定长的字段类型,直接分别读取一位,这样一条数据就顺序读取到了。

  • null值列表:一行数据的字段不光会出现变长字段,也可能存在null值的情况,这些可以为null的字段它们的长度也不是固定的,只要字段长度不是固定的,就会造成预估要读取的数据长度不确定。


       设计null值列表,用一个bit位来标记字段是否为null,1:表示字段为null值,0:表示字段不为null值,如一行数据为 null a a 对应的null值列表应该为[100],和变长字段长度列表的逆序排列特征⼀样,null值列表逆序存放就为[001],但是一般至少也是8个bit位即一个字节的大小,所以前面补全就是[00000001]

    引入null值列表之后,如果读取一行数据时,某个字段可以为null,就先从null值列表中获取该字段对应的bit位,bit位为1字段的值就为null,跳过不读取,bit位为0则字段的值不为null,此时该字段又为varchar字段,再从变长字段长度列表得到字段的长度,完整的读取数据,这样一行数据中不确定的两个因素:varchar字段、null值,就通过引入变长字段列表和null值列表解决了。


  • 数据头:一行数据的数据头用来描述该行数据的,数据头由40个bit位组成。




    引入数据头之后,一行数据在磁盘中的大概如下 :

  • 实际数据:会先解析成字符编码的样式存放


    实际数据中有几个隐藏字段分别如下:

  • DB_ROW_ID:表示行的唯一标识,一般情况都是主键,如果没主键会自动生成一个,目的是唯一标记该行数据

  • DB_TRX_ID:事务ID,记录最近一次被那个事务修改操作过,该标记在MVCC机制有非常大的作用

  • DB_ROLL_PTR:表示回滚指针,在事务回滚时与undo log 有关

二、一行数据在数据页中的存储

数据页的数据存储模型:


    当数据页中没有数据时,数据区几乎不占内存,随着一行行数据进入数据页,空闲区的内存被耗完,一个数据页的内存大小为16kb,如果一行数据中的一个字段的大小,它占的内存远远大于一个数据页的16kb大小时,此时一个数据页存放不下,会让多个数据页共同存放这行数据,原来的数据页先存放一部分数据,然后用20个字节的一个指针指向其他共同存放该字段数据的其他数据页,共同参与数据存放的数据页被称为溢出页。


数据页在表空间中的划分和存储

数据表只是一个逻辑上的一个概念,在实际的物理存储上,mysql innodb存储引擎的设计中,一张表则对应一个表空间,同时一个表空间就映射到磁盘中的一个表空间文件,表空间文件以.ibd为文件后缀。

而数据页存放在表空间中,但一个数据页才16kb大小,大量的数据页堆在表空间不好管理,引入数据区的概念:一个数据区中可以存放64个数据页即一个数据页大小为1MB大小。


256个数据区可以被划分一个数据区组,多个数据区组形成了表空间的整体数据结构;


经过以上对一行数据、一个数据页、表空间的分析,一个整体流程图如下:

image.png



redo log 和 undo log的数据存储模型

    redo log引入redo log block来存放一条一条的redo log日志,每条redo log日志都是先存放在redo log block的body区中。


   对于一个事务,一般都会有多条redo log日志信息,多条redo log 日志信息存放在512字节的redo log block中的body中,当body中的496字节的内存存满后就存放下一个redo log block,然后这些redo log block会先存放在内存中的redo log buffer缓存中,默认大小为16MB,可通过参数innodb_log_buffer_size设置,然后到一定的时机再刷到磁盘文件中,redo log日志文件对应磁盘mysql安装目录下的ib_logfile0和ib_logfile1两个文件,默认只有两个文件,每个文件大小上限都为48MB,写完一个就写下一个循环着,同一时刻最多只能存放96MB的redo log日志信息。


对于redo log刷盘时机,一般有以下四种情况:

  • 如果一次性redo log buffer的内存超过一半,默认超过8MB:该情况下主要发生在高并发场景,大量的事务执行导致一下子redo log buffer内存过大,直接导致刷盘

  • 提交事务的情况下会刷盘:对于事务的提交,只有依赖redo log刷盘持久化才能保障事务的可靠性

  • 后台线程定时redo log buffer刷盘:类似buffer pool链表刷盘一样,定时会有后台线程将redo log block数据顺序写入磁盘中

  • mysql停止服务时触发刷盘


文章转载自菜涛学Java,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论