InnoDB 系列文章目录
1.InnoDB的关键特性
2.InnoDB的存储结构
3.InnoDB的索引和算法
4.InnoDB的锁总结
5.InnoDB的事务
上一篇文章
1.InnoDB的关键特性
介绍了InnoDB的四大特性,这篇文章介绍下InnoDB的存储结构。
一 物理文件
首先,先打开mysql的数据存储路径,可以使用命令查看具体的路径。
如果是使用Navicat Premiun,可以直接按下F6打开命令界面。
show global variables like "%datadir%";
表空间文件 ibd
可以看到里面有一些文件,其中以ibd为后缀的文件,就是存放数据和索引以及一些其他数据,称之为表空间。如果将innodb_file_per_table 设置为ON(默认),那么每个表都有一个独立的表空间,打开mydata文件,可以看到以表命名的ibd文件。
除了ibd,还有一些日志文件,如二进制日志:binlog.xxxxx,参数文件,表定义文件等。
二 逻辑结构
既然知道了数据的物理文件,那么表空间内的逻辑机构又是如何呢?
段
表空间了包含了多个段,常见的包括索引段,数据段,回滚段。索引段存储了非聚集索引,而数据段其实是按照聚集索引存储的数据,所以可以理解为格式是一样的。
每个段中包含了区和32个页,一般先使用页,当页使用完了才使用区,防止每次创建一些数据时,需要申请很大的一个区的空间。
区
区是由64个连续的页组成的,每个页16KB,所以一个区就是1MB。最多可以申请4个区以保证数据的连续性。
页
页是具体存储数据的单位,实际上索引并没有找到对应的数据的行,而是找到对应数据的行所在的页。然后把页加载到缓冲区后,使用二分法查找对应的行数据。
页可以存储数据,也可以存储UNDO,索引,还有大对象。
行
行就是我们一般理解的数据库中的一行数据,存放在页内,一页存放16KB的数据,也就是2~200行。
三 行记录格式
介绍完InnoDB数据存储的段区页行,再介绍下行的具体格式。
InnoDB又两种存储数据的行格式,Compact和Redundant,Redundant时为了兼容之前的版本而保留的。
1.Compact格式
Compact行数据格式分为四个部分
-
变长字段长度列表
变长字段长度列表记录了变长字段(varchar)的长度,并且是以倒序存放的。每一个变长字段最长用两个字节表示,所以varchar的最长长度是65535(2^16-1)。但是实际上,如果创建一个varchar(65535)的字段时,会发现mysql有警告,而且在非严格模式下,会发现该字段的类型被转换为text。那是因为实际上还有其他的消耗。实际上能创建的最大的长度是65532. -
NULL标志位
NULL标志位,标记哪个字段的值为空。有没有好奇过mysql是如何存储NULL值,实际上NULL值并没有被真正的存储,而是用一个标记位来标记那一个字段的值为null,这个标记位用二进制表示,0为非null,1为null。而在实际的列数据中,并没有占用空间,(注意,这是Compact格式下,Redundant格式下,char的null值还是会占用空间)。 -
记录头信息
-
列数据
列数据除了保存有用户定义的数据,还包含了两个隐藏列,事务ID列和回滚指针列,如果表没有指定主键,还会创建一个6格字节的RowID作为默认主键。另外,如果创建了视图,还有有视图的事务ID,回滚指针和RowID,所以总共会有6个保留隐藏列。
2.Redundant格式
null值的存储
可以看到,相比Compact,少了一个NULL值标记位。那么Redundant格式是如何保存null值的
- 对于varchar类型
字段长度偏移列表中记录了varchar的长度,也就是0,所以在列数据中,varchar实际页不占用空间 - 对于char类型
字段长度偏移列表中记录了char的固定长度,所以并不是0,所以在列数据中,char会实际占用空间。而且,char会占用可能存放的最大空间。
可以看出,相对于Compact,Redundant存储格式的null值更占用空间。
最大列数
另外值得注意的一点是,在Redundant格式中记录头信息中,有个字段n_fields,记录了列数。
可以在图中看出,n_fields是一个占用10个bit大小的值,因此,一个表最大列数只能是1023(2^10-1),但是如果你还记得上面Compact说过,实际上还有6个隐藏的列,所以实际上,用户最多只能创建1023-6=1017列。
不过令人存疑的是,Compact格式下并没有n_fields,那么如果表是Compact格式,是否能够创建的列就不受限制呢?于是我尝试创建了一个1018列的Compact格式的表,但是依然提示错误“Too many columns”;可能是为了兼容,InnoDB做了限制。不过一个表这么多列,也不是一个好的数据库表设计。
char的保存格式
另外,从mysql4.1开始,char(2)括号中的数字指的是字符的长度而不是字节的长度,比如“中文”,字节长度为4,字符长度为2,所以一个char(2)的字段是可以存储“中文”这个字符串的。所以实际上,char的存储方式跟varchar的存储方式是一样的,也是变长的。
行溢出数据
一般我们认为,对于text,blob数据,并不会和其他数据存放在同一页,而是单独存放在一个页里面,但是实际上真的是这样吗?
实际上,如果长度小于8089,那么,并不会存放在单独的页里面,而是直接存放在行数据所在的页。即使超出了阈值,也不是全部存放在新的页,而是任然会保留部分数据在行数据所在的页(前768个字节)。