MySQL 在磁盘上如何存储数据的

一、一行记录的格式

首先介绍“行格式”这个概念,我们可以通过行格式来指定表的行存储的格式是什么样的。常见的行式有以下4种:
(1)REDUNDANT
(2)COMPACT
(3)DYNAMIC
(4)COMPRESSED

各种表格式的特点及缺点:
hgs
以COMPACT为例,在这种格式下,每一行数据在实际存储时,打给的格式类似于下面这样:
变长字段的长度列表,null值列表,数据头,column01的值,column02的值,…,column0n的值
注意实际存储的时候没有逗号分隔,这里只是为了方便说明。

1. 变长字段的长度列表

MySQL的字段是支持变长字符串的,如果直接保存到磁盘文件中,变长字段如何保存的呢,我们怎么知道每个字段的起始位置呢。其实,MySQL中是通过加上变长字段的长度列表这个附加信息来解决的。例如有下面这两行记录:

name(varchar(100))website(varchar(100))
baiduwww.baidu.com
tencentwww.tencent.com

首先"baidu"这个字符串长度为5,就用5的十六进制0x5来表示,"www.baidu.com"这个字符串的长度为13,就用0xd来表示。那么这行记录的COMPACT格式就是这样的:0x5 0xd null值列表 数据头 baidu www.baidu.com。同理第二行记录是这样保存的:0x7 0xf null值列表 数据头 tencent www.tencent.com。两行记录连在一起就是:

0x5 0xd null值列表 数据头 baidu www.baidu.com 0x7 0xf null值列表 数据头 tencent www.tencent.com

这样我们可以很容易的根据变长字段列表来读取每个字段了。

2. NULL 值字段列表

有的表示允许一行记录中某些字段为null的,但是实际存储的时候,不可能用一个"NULL"字符串来表示空值。MySQL是通过二进制的bit位来保存NULL值字段的。例如有下面这两行记录:

name(varchar(100) not null)address(varchar(100))gender(char(1))school(varchar(100))job(varchar(10))
tomnull1nullteacher
jerrybeijing1nullstudent

其中,name不允许为null,其他字段都允许。第一行记录有两个空值,那么就用 1010来表示,1代表这个位置的值为空,0代表不空。实际存储的时候是逆序的,也就是存储为0101。所以第一行记录在磁盘上保存为:0x3 0x7 0101 数据头 tom 1 teacher。同理第二行记录保存为:0x5 0x7 0x7 0100 jerry beijing 1 student。两行记录连在一起就是:

0x3 0x7 0101 数据头 tom 1 teacher 0x5 0x7 0x7 0100 jerry beijing 1 student

这样我们就可以通过null值字段列表来判断值为空的字段了。

结合上面的变长字段列表和这里的NULL值字段列表,我们就知道从磁盘上读取一行数据的过程如下:

  • 读取解析变长字段列表和NULL值字段列表
  • 如果一个字段是NULL值,那么就直接设置为NULL
  • 如果一个字段不是NULL,并且是定长字段,那么直接读取固定长度的内容
  • 如果一个字段不是NULL,并且是变长字段,那么根据变长字段列表中这个字段的长度来读取内容
    这样就得到了最终的一行记录的所有字段的值了。
3. 数据头

数据头占40个bit位,它是用来描述这行数据的信息的:

  • 第1个bit位和第2个bit位是预留的,没有实际意义
  • 第3个bit为是delete_mask:表示这行数据是否被删除
  • 第4个bit位是min_rec_mask:表示B+树中每一层非叶子节点里的最小值
  • 第5-8的bit位是n_owned
  • 第9-21的bit位是heap_no:表示这行数据在记录堆里的位置
  • 第22-24的bit位是record_type:表示这行数据的类型。0代表普通类型,1代表B+树的叶子节点类型,2代表是最小值数据,3代表是最大值数据
  • 第25-40的bit位置next_record:是指向下一条数据的指针

二、磁盘上如何存储一行记录

假设我们这里有一行数据是"jack NULL m NULL xx_school",那么它的行格式如下:

0x9 0x4 0000101 0000000000000000000010000000000000011001 jack m xxschool

但是实际上存储到磁盘里的时候并不是上面这个格式,而是根据数据指定的字符集编码经过编码之后再进行存储。具体如下:

0x9 0x4 0000101 0000000000000000000010000000000000011001 616161 636320 62626262

同时还会在真实数据之后加入一些隐藏字段,包括:

  • DB_ROW_ID:一行记录的唯一标识,如果你没有手动设置主键,那么数据库就会用这个字段作为主键
  • DB_TRX_ID:事务ID,表示哪个事务更新的这行数据
  • DB_ROLL_PTR:回滚指针,是在事务回滚时用到的

所以磁盘上最终保存的数据大概是下面这个样子的:

0x9 0x4 0000101 0000000000000000000010000000000000011001 000000000094C 000000000032D EA0000010078E 616161 636320 62626262

这里的000000000094C 000000000032D EA0000010078E 分别是就是DB_ROW_ID,DB_TRX_ID,DB_ROLL_PTR这三个字段。

三、磁盘上的数据页

我们知道每次从磁盘加载到Buffer Pool以及刷回磁盘的时候,都是通过数据页来进行操作的。而数据页中又包含了上面说的一行一行的数据。每个数据页的大小是16KB,除了行数据之外还包含其他一些信息:文件头、数据页头、最大记录和最小记录、空闲区域、数据页目录以及文件尾部。如下图:
diskdatapage
这个就是磁盘上一个空的数据页的组织格式,这时候还没有插入数据进来。如果此时我们想要插入一行数据,那么肯定是加载一个空的数据页到Buffer Pool中,然后将这条数据插入Buffer Pool中的空闲缓存页,然后再刷回磁盘,这样一行记录就保存到磁盘中的数据页中了。如下图:
record

四、表空间和数据区

1. 表空间

表空间就代表着我们创建的某个表所对应的那些数据页,因为一个数据页的大小为16KB,所以不可能一个数据页就是一个磁盘文件。所以一个表空间的磁盘文件里是有很多数据页的。但是可能表空间包含的数据页是在太多了,不便于管理,所以MySQL又引入了一个数据区(extent)的概念。

2. 数据区

一个数据区对应了64个数据页,每个数据页16KB,所以一个数据区是1MB的大小。然后256个数据区又被划分成一组。

对于表空间的第一组数据区的第一个数据区的前3个数据页,都是固定的,存放了一些描述信息。表空间其他组数据区的第一个数据区的前2个数据页也是存放固定的特殊信息的。

所以表空间的实际存储结构大概如下图:
table_space


THE END.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值