这篇,我们要回答如下问题:
InnoDB 数据库表存放在磁盘的什么地方?
InnoDB 表空间是什么样的?
InnoDB中一行数据怎么存储的?
varchar 是真么保存长度的?
Null 值是如何保存的?
先看图后说话:
InnoDB 磁盘存储结构概述
什么是表空间?
这是MySQL 中的一个概念,MySQL 将数据文件有组织的存放在一个空间中,这个就叫做表空间。表空间由 {段{区{页{行}}}} 构成。行是数据存储的基本单元,页是MySQL 磁盘读写的基本单元,MySQL 一页默认配置大小16k, Linux 默认配置 页大小是4k。具体查询见下:
查看MySQL 表空间存储位置(创建的表存储位置)
show variables like 'datadir';
sudo ls -lha /var/lib/mysql | grep MySQLTest
sudo ls -lha /var/lib/mysql/MySQLTest
查看MySQL页配置大小
show variables like 'innodb_page_size';
查看Linux 系统页配置
getconf PAGE_SIZE
表空间由几大部分组成?
如上图,InnoDB 在盘中的存储机构可划分为六个部分:
- 系统表空
- 双写文件
- undolog表空间
- Redolog
- file-per-table 独立表空间
- 通用表空间
- 临时表空间
系统表空间
系统表空间是 数据字典、双写缓冲文件、修改缓冲文件、回滚日志的存储位置。如果关闭独立表空间,所有表数据文件和索引文件页将存储在此处。这里简单概括上面四个文件:
- 数据字典: 各种表的元数据信息(表结构,索引,列定义 -- 简单来说就是表DDL信息存)组成的表.
- 双写缓冲文件: 双写缓冲用于保证数据在磁盘中的完整性,防止部分写失效的问题。
- 修改缓冲: 内存中change buffer 对应的磁盘持久化区域
- undolog: 实现事务回滚,MVCC的重要组成
file-per-table 独立表空间
- 存放每个表的数据,索引,插入缓冲的地方。开启表空间后,系统会为每个数据库单独创建一个文件夹,用于存储每个表的.frm文件和.idb文件。
- 初始表空间默认大小96k
undolog表空间
- 存放undolog的地方,默认大小10M,超过会分裂
redolog
- 崩溃恢复文件
临时表空间 - 5.6 之后抽离处理的,原来和系统存在一个地方,会引起系统表空间过度膨胀。
- 独立保存临时表数据和会滚信息。
通用表空间
- 独立表空间可以转化为系统表空间,但是系统表空间不可转换为独立表空间。通用表空间是其中的桥梁,通过表空间实现二者可以相互转换。
磁盘存储结构
- tablespaces(表空间),可以理解为一个逻辑空间,实际上不一定实体存在。具体表现为在开始独立表空间配置时,mysql会为每个数据空单独创建一个文件夹,用具存放.frm和.idb,这个和数据库同名的文件夹就是独立表空间
- segment(段),表空间由各个segment段组成,段的类型可以分为数据段、索引段、元信息段、回滚段等。一个分段包含多个(extent)分区
- extent(分区),分区由连续的页组成大小默认1M,由于页默认大小16k,所以一个分区由64个页。为了页连续性,mysql一次会从磁盘中那个申请4-5个分区。
- page (页),磁盘基本存储单位默认配置16k,4个系统页4k 组成一个mysql 页
- row (行), 数据记录的基本存储单位,innodb支持4中行格式,默认Dynamic格式.
到这里算是介绍完了磁盘存储结构,接下俩就是要回答上面的问题。
InnoDB 数据库表存放在磁盘的什么地方?
show variables like 'datadir';
show variables like datadir
; 可以查看默认配置路径,linux 默认配置路径/lib/usr/mysql 下,可在数据库都次启动前修改my.cnf 对应字段指定位置,独立表空间可以在创建时指定位置。
InnoDB 表空间是什么样的?
见上面表空间介绍。
InnoDB中一行数据怎么存储的?
innoDB 在数据页中行格式如下:
变长列长度表(定义长度)|实际数据为null列bitmap|record head info 记录行头信息|row_id: 没有唯一键、没有主键时的隐藏行|trx_id 事务id|roll_ptr|data|
在数据行中,记录行的记录头(变长列数据长度|实际数据为null列bitmpa|record head info 记录行头信息|) 数据存储顺序和真是数据记录顺序相反,记录头中的对应表中列的位置刚好相反,即表中第一类的对应记录位在记录头的某一段中的位置是最后一位,记录头的倒数第二位是表的顺数第二位;具体示意看下图:
下面是一行数据的每个段的简单介绍,顺序从左向右:
变长字段表(定义长度):
- 若表中无变长字段定义,变长字段不存在
- 底层存储编码数据变长类型的 varchar\text\middle text\long text 其中text 族时长文本类型,有出现字段溢出处理。
- 在ascii 编码下(一字符占一字节,在计算处asscii 最大大小/编码字符占位大小) varchar 最大长度 是 65535 - 2 - 1 = 65532.
- 若为表中有N个变长字段 某变长字段最大长度:655535 - N3;
- 表中所有变长字段定义长度+ Null 字段+ 变长字段必须 < 65535
- 某个列的定义长度一个字节(256)表示不下时会升级成2字节...继续记... 直到记完
空值字段表:
- 表中所有类都有非空约束,这个字段不存在。有类可为空值这个段一定最少占1个字节,可用0表示这行记录所有字段都有内容。
- 记录顺序和变长列记录顺序相同,表是一个字节的无符号位图,比特位置为1表示这个字段在这行记录空,0表示这个字段在这行记录中有内容
- 一个字节可以表示8个字段,记不下会加一个字节,继续记... 直到记完
记录头信息
记录头信息里有很多记录段这里主要分享delete_mask\next_record\record_type
- delete_mask: 删除标记位
- next_record: 下一条记录的地址 指向一个记录位置,指向位置时记录头末尾,真实数据开头的地址
record_type: 记录类型,0: 普通记录, 1:B+Tree 非叶子节点记录, 2:最小记录,3:最大记录
row_id
- 表中有定义主键、或有唯一约束键,此段存在,占6字节
- 上面都没有时,InnoDB 会自动定义一个隐藏主键,这个记录的就是隐藏主键
rtx_id
- 生成、操作过这段这行记录的last_rtx_id,占6字节
roll_ptr
- 这行记录的上一个版本地址,这个时必须存在的。占7字节
一句话概括innoDB 怎么存储的
一条数据存在表空间中(开始独立表空间的情况下存在数据库同名文件夹下的表名.idb下,不开启会存在系统表空间下)的某个数据文件的数据页中的某一行,这一行数据会按如下格式存储在文件中:
变长列定义段|空值段|记录信息头段|row_id|rtx_id|roll_point|数据段|
其中变长列定义段、空值段、row_id、是可选段根表定义和数据当前行数据请款出现。
变长定义段、控制段的记录顺序和数据段记录顺序相反。相反的原因是为何提高在cpu cache line 中的缓存命中率。
varchar 是怎么保存长度的?
- 逐字节扩展的存储方式,定义长度0-255 用一个字节保存,超过升级位2字节,依次类推
Null 值是如何保存的?
- 某类值为空,对应null 段表的对应位图映射位记为0
一条记录,如果包含的text/blob 这种长文本对象时,长度超过65536,出现行溢出会如何处理?(compact和dynamic 编码区别是什么?)
出现行溢出时compact 和 dynamic 对应处理如下:
- compact : 数据段中记录的数据只记录原数据的一部分,后面余下20字节空间记录溢出内容存在溢出为文件的地址
- dynamic:溢出后直接把全部数据转移到溢出页,只留20字节记录溢出页对应地址。
compact和dynamic 编码区别是什么?
最大区别是对长文本溢出处理的处理,compact仍会记录一部分数据后面留20字节记录溢出内容在溢出页地址;dynamic 则会把整段内容直接移到溢出页,原来的地方只留20字节记录溢出内容在溢出页地址值。