Mysql 数据如何储存?
数据目录
当创建数据库时会创建数据目录下的子目录,并且在该子目录下创建db.opt文件,包含该库的各个属性,比如字符集、比较规则等
当创建表时,会创建后缀frm的表结构进行储存表结构,而实际数据则储存在表空间中
而在innoDB中表空间分为两类
- 系统表空间:在5.5.7~5.6.6采用一份系统表空间储存数据
- 独立表空间:在5.6.6之后,为每个表创建独立表空间。比如对于创建表tb,会创建tb.ibd文件进行储存实际数据
表空间
在InnoDB中表所有数据储存到表空间中
系统表空间
储存额外系统信息的唯一独立表空间
独立表空间
独立表空间相比于系统表空间具有以下优势
- 更省空间:在表删除之后可以直接回收硬盘空间
- 空间上限更高:每个独立表空间都可以有64TB大小限制
- truncate table性能更佳
缺点如下
- fsync操作总数可能增加:因为fsync是对每个文件操作的
- 表空间文件句柄、描述符增加
- 可能浪费空间,因为只能被同表行使用
值的注意的是独立表空间储存只是数据、索引和插入缓冲bitmap页,其他如回滚信息、插入缓冲表、二次写缓冲等还是存放在原来的共享表空间中
常规表空间
常规表空间是由CREATE TABLESPACE创建的innoDB表空间
常规表空间是共享表空间,能在多种表空间之间进行迁移
段
段分为索引段、数据段、回滚段等。其中索引段是非叶节点部分,而数据段则是叶节点部分,回滚段则用于数据回滚和多版本控制
区
区是由连续页组成,连续64页就是一个区,也就是默认占用1MB空间。
连续页的好处在于利用硬盘更快的顺序IO性能
在最初创建表后,段先采用32页大小的碎片页进行存放数据,在使用完之后才进行连续64页的申请
这样对于那种小段能很好地节省和利用空间
此外,为了方便区管理,还引入了XDES储存区信息,并根据不同状态区XDES entry连接起来,那么当我们想找空闲区就可以很快找到空闲状态的链表项,取空间使用
页
页是InnoDB的硬盘管理最小单元,默认大小为16KB
常见页类型如下
- 数据页(B-tree Node)
- undo页(undo log page)
- 系统页(system page)
- 事务数据页(transaction system page)
- 插入缓冲位图页(insert buffer bitmap)
- 压缩的二进制大对象页(compressed BLOB page)
数据页结构由以下组成
- file header(38字节)
- page header(56字节)
- infimum + supermum: 储存伪记录,限定记录的边界
- user records: 实际储存记录的地方
- free space: 空闲储存空间
- page directory: 页目录,用于加速页记录寻找
每当插入一行记录时都会从free space申请空间划分到user space
页目录
页目录储存记录的相对位置,在一个槽中可能包含多个记录
对于最小记录所在分组只能有一行记录,最大记录所在分组拥有记录行数在1~8之间,其他分组则在4~8之间
查找过程是通过二分查找定位到所在组,然后遍历整组的记录
页头部
储存页储存记录数量、第一条记录地址、页目录数量等
File Tailer
页完全修改之后,生成校验和写入到trailer,若header、trailer校验和不一致,说明修改过程中出现了错误
行
InnoDB是按照行进行存放的。其格式分为compact、redundant、dynamic、compressed
其中compact、redundant来自于1.0.x之前的Antelope,而dynamic、compressed则定义为barracuda
Dynamic、Compressed在处理溢出页的时候会直接储存指向溢出页的地址,而compressed则会更一步压缩
以下仅介绍compact格式
-
记录额外信息
-
变长字段长度列表:逆序储存变长字段(比如varchar,或者如果字符集也是变长的)占用字节长度(但不储存值Null的列长度)
-
NULL值列表
- 统计表中允许储存NULL的列,排除主键、not null字段
- 逆序储存是不是NULL,是为1,不是为0
- 不足字节位,在高位补0
-
记录头信息
-
预留
-
delete_mark: 标记是否被删除
-
next_record: 到下条真实记录数据的偏移量
之所以是记录到下条真实数据的偏移量,而不是下条数据偏移量的原因在于可以利用局部性将前面数据的逆序储存的字段长度、NULL值列表缓存起来,减少数据再次查询硬盘次数
-
min_rec_mask: 非叶节点的最小记录标记
-
heap_no: 记录在本页的位置
-
record_type: 记录类型。0代表普通,1代表B+树非叶节点,2表示最小记录,3代表最大记录
-
n_owned: 表示本组拥有几行记录
-
记录真实数据
列1、列2等值+隐藏列(row_id行id、transaction_id事务id、roll_pointer回滚指针)
行溢出数据
varchar(M)最多能允许储存的数据量。我们知道varchar类型的列最多允许65535字节,在允许NULL的情况下最多储存65532字节,因为NULL标识符占1字节,真实长度数量占2字节
M记录的是字符长度,那么在utf8情况下,表示一个字符最多3字节,则M最大值为65532/3=21844
此外,65532这个限制是所有varchar列的长度总和,也就是如果存在多个varchar字段,虽然单个没有超出限制,但这多个字段如果合起来超出了限制,那还是会无法创建的
在超过页大小16384字节时,会记录数据前768字节,和溢出页地址(指向uncompressed blob page溢出页)
char行储存结构
char(N)中N代码字符长度,这也就意味着不同字符集下储存的并不是定长的数据。这就使得innoDB内部将char视为变长字符类型
Ref
- https://blog.csdn.net/u010647035/article/details/105009979
- 《Mysql技术内幕(InnoDB储存引擎)》
- 《Mysql是怎样运行的》
-
-
-