这篇,我们要回答如下问题:

InnoDB 数据库表存放在磁盘的什么地方?

InnoDB 表空间是什么样的?

InnoDB中一行数据怎么存储的?

varchar 是真么保存长度的?

Null 值是如何保存的?

先看图后说话:

InnoDB architecture diagram showing in-memory and on-disk structures. In-memory structures include the buffer pool, adaptive hash index, change buffer, and log buffer. On-disk structures include tablespaces, redo logs, and doublewrite buffer files.

InnoDB 磁盘存储结构概述

什么是表空间?

这是MySQL 中的一个概念,MySQL 将数据文件有组织的存放在一个空间中,这个就叫做表空间。表空间由 {段{区{页{行}}}} 构成。行是数据存储的基本单元,页是MySQL 磁盘读写的基本单元,MySQL 一页默认配置大小16k, Linux 默认配置 页大小是4k。具体查询见下:

image-viao.png

查看MySQL 表空间存储位置(创建的表存储位置)

image-xhqz.png

show variables like 'datadir';

image-krmg.png

sudo ls -lha /var/lib/mysql | grep MySQLTest
sudo ls -lha /var/lib/mysql/MySQLTest

image-nbwf.png

查看MySQL页配置大小

show variables like 'innodb_page_size';

image-cdjn.png

查看Linux 系统页配置

 getconf PAGE_SIZE

表空间由几大部分组成?

如上图,InnoDB 在盘中的存储机构可划分为六个部分:

  • 系统表空
  • 双写文件
  • undolog表空间
  • Redolog
  • file-per-table 独立表空间
  • 通用表空间
  • 临时表空间

image-cjkt.png

系统表空间

系统表空间是 数据字典、双写缓冲文件、修改缓冲文件、回滚日志的存储位置。如果关闭独立表空间,所有表数据文件和索引文件页将存储在此处。这里简单概括上面四个文件:

  • 数据字典: 各种表的元数据信息(表结构,索引,列定义 -- 简单来说就是表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|

无标题-2024-10-27-1919-1.png

在数据行中,记录行的记录头(变长列数据长度|实际数据为null列bitmpa|record head info 记录行头信息|) 数据存储顺序和真是数据记录顺序相反,记录头中的对应表中列的位置刚好相反,即表中第一类的对应记录位在记录头的某一段中的位置是最后一位,记录头的倒数第二位是表的顺数第二位;具体示意看下图:

image-lkzu.png

下面是一行数据的每个段的简单介绍,顺序从左向右:

变长字段表(定义长度):

  • 若表中无变长字段定义,变长字段不存在
  • 底层存储编码数据变长类型的 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: 下一条记录的地址 指向一个记录位置,指向位置时记录头末尾,真实数据开头的地址

无标题-2024-10-27-1919.png

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字节记录溢出内容在溢出页地址值。