初涉mysql物理数据格式:一行数据在磁盘上是如何存储的
- 行格式:对一个表指定他的行存储的格式是什么样的,在建表的时候,就可以指定行存储的格式,后面也可以修改行存储格式
create table table_name(columns) row_format = compact
alter table table_name row_format = compact
# 大概格式类似
边长字段的长度列表,null值列表,数据头,column1的值,cloumn2的值,....
对于每一行数据,他其实存储的时候都会有一些头字段对这行数据进行一定的描述,然后再放上他这一行数据每一列的具体的值,这就是所谓的行格式。
对于VARCHAR这种边长字段,在磁盘如何存储
- 数据再磁盘文件里,数据是挨在一起存储的
所以再存储每一行数据的时候,都要保存一下他的变长字段的长度列表,才能解决一行数据的读取问题。- hello a a记录的时候要记录成0X05 null值列表 数据头 hello a a
- 如果这个时候有2行数据,hi a a 会被记录成:0X05 null值列表 数据头 hello a a 0X02 null值列表 数据头 hi a a
- 如果有多个变长字段,此时在磁盘中存储的,必须在他开头的边长字段长度列表中存储几个变长字段的长度,而且是逆序的
- eg varchar(10), varchar(5), varchar(20),char(1),char(1) 一共5个字段,比如一行数据是hello hi hao a a;他存储是这样的 0x03 0x02 0x05 null值列表 头字段 hello hi hao a a
一行数据中的多个null字段值在磁盘上怎么存储
- null值是以二进制bit位来存储,对所有的NULL值,不通过字符串在磁盘上存储,而是通过二进制的bit位来存储。
create table customer(
name varchar(10) not null,
address varchar(20),
gender char(1),
job varchar(30),
school varchar(50)
) row_format=conpact;
- 如果我要来存
JACK NULL m NULL xx_school
- 首先变长字段有4个,我们先逆序排序变长数组 null的就不用存,
0x09 0x04 null值列表 头信息 hack m xx_school
- 接下来看null值列表,所有允许的null值的字段都对应一个二进制bit位的值,如果bit为1说明是null,如果是bit值是0说明不是null 。所以4个bit位为
1010
,然后放null值列表的时候,也要逆序,所以null值列表是0101 - 所以数据应该是
0x09 0x04 0101 头信息 hack m xx_school
,又应为null值列表存放的时候,不会说仅仅是4个bit位,他一般起码是8bit位的倍数,如果不足8个bit就高位补0,所以实际上存放看起来是:0x09 0x04 00000101 头信息 hack m xx_school
- 首先变长字段有4个,我们先逆序排序变长数组 null的就不用存,
磁盘文件中,40个bit位的数据头已经真实数据是如何存储的
- 没遗憾数据存储的时候,还有40个bit位的数据头,这个数据头是用来描述这行数据的。
- 第1,第2个bit位都是预留位,是没有任何含义的。
- 第3个bit位是
delete_mask
,他标识的是这行数据是否被删除了,在mysql里删除一行数据的时候,未必是立马被他从磁盘上清理掉,而是给他在数据头里搞1个bit标记他是否删除 - 第4位是
min_rec_mask
:B+数每一层的非叶子节点里的最小值的标记 - 第5-8位
n_owned
:记录数 - 第9-21位
heap_no
:代表这行数据在记录堆里面的位置 - 第22-24位
record_type
:这行数据的类型;0代表普通类型,1代表的是B+树非叶子节点,2代表的是最小值数据,3代表的是最大值数据。 - 第25-40位
next_record
:指向下一条数据的指针
每一行实际数据在磁盘上是如何存储的
- 实际上字符串这些东西会根据我们数据库指定的字符集编码,进行编码之后再存储,所以数据大概是
0x09 0x04 00000101 00000000000000000000000000000000000000000(40位bit 头信息) 616161 636320 6262626262
- 我们的字符串和其他类型的数值最终都会根据字符集编码,搞成一些二数字和符号存储再磁盘上,再实际存储一行数据的时候,会在他的真实数据部分加入一些隐藏字段。
DB_ROW_ID
:一个行的唯一标识,他是数据库内部给你搞的一个标识,不是主键ID字段。如果没有指定主键和unique key唯一索引的时候,他就内部自动加一个ROW_ID作为主键DB_TRX_ID
:事务idDB_ROLL_PTR
:回滚指针
- 所以如果加上这几个隐藏字段后,实际一行数据可能看起来如下
0x09 0x04 00000101 00000000000000000000000000000000000000000(40位bit 头信息) 00000000094C(DB_ROW_ID) 00000000032D(DB_TRX_ID) EA00001007E(DB_ROL_PTR) 616161 636320 6262626262
行溢出
- 如果一行数据的大小超过了数据页的大小,就会发生行溢出,这个时候实际上会在那一页存储这行数据,然后在那个字段中,仅仅包含他一部分数据,同时包含一个20个字节的指针,指向了其他的一些数据页,那些数据页用链表串联起来,存放放不下的数据,
- 一行数据存储的内容太多了,一个数据页都放不下了,此时只能溢出这个数据页,把数据溢出存放在其他数据页里去,这些数据页就叫做溢出页
用于存放磁盘上的多行数据的数据页到底长什么样
- mysql进行数据操作的最小单位是数据页,一个数据页分为多个部分:
- 一开始如果还没有存入数据,是没有数据行这个空间的,然后加载的时候把数据页的所有数据都加载到缓存页,对缓存页的数据进行操作CRUD,缓存页会定期刷入磁盘。
表空间以及划分多个数据页的数据区
- 我们平时创建的那些表,其实都是有一个表空间的概念,在磁盘上都会对应着"表名.ibd"这样的一个磁盘数据文件;在物理层面,表空间就是对应一些磁盘上的数据文件。
- 有的表空间,eg系统表空间可能对应的是多个磁盘文件,有的我们自己创建的表空间可能就是对应一个"表名.ibd"数据文件
- 在表空间的磁盘文件里,其实会有很多很多的数据页,由于一个表空间的数据页太多了,所以表空间又引入了数据区的概念,一个数据区对应连续64个数据页,每个数据页16kb,所以一个数据区1mb,然后256个数据区划分为一个组。
- 对于表空间,第一组数据区的第一个数据区的前3个数据页都是存放一写描述性数据。
FSP_HDR
:存放了表空间和这一组数据区的一些属性IBUF_BITMAP
:存放的是这一组的所有insert buffer的一些信息INODE
:存放一些特殊信息
- 表空间的其他各组数据区的第一个数据区的头两个数据页都存放特殊信息
- 我们平时创建的那些表都是由对应的表空间,每个表空间都是对应了磁盘上的数据文件,在表空间里有很多组数据区,一组数据区是256个数据区,每个数据区包含了64个数据页。当我们执行crud操作的时候,就是从磁盘上的表空间的数据文件里,去加载一些数据页出来到buffer pool的缓存页里去使用。