主要介绍InnoDB存储引擎表的逻辑存储以及实现。重点介绍数据在表中是如何组织和存放的。
1.索引组织表(index organized table)
在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表叫索引组织表。在InnoDB存在引擎表中,每张表都有个主键(Primary key),如果在创建表时没有显示定义主键,则会按照如下方式选择或者创建主键:a.判定是否有非空的唯一索引(unique not null),如果有则该列即为主键。若果有多个,则选择建表是第一个定义的非空位于索引为主键。注意:主键的选择根据的是定义索引的顺序,而不是建表时的列的顺序。 b.如果不存在唯一索引,InnoDB存储引擎字段创建一个6字节大小的指针。
2.InnoDB逻辑存储结构
在InnoDB存储引擎中,所有的数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称之为块(block)。InnoDB存储引擎的逻辑存储结构如下图。
下边我们就分别介绍一下。
A.表空间
可以看见InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。表空间又分为独立表空间和共享表空间。通过参数innodb_file_per_table参数来决定使用何种类型的表空间。但是需要注意的是独立表空间内只存放数据、索引和插入缓冲页,其他的数据,如回滚(undo)信息、插入缓冲索引页、系统事务信息、二次写缓冲(double write buffer)等还是放置在原来的共享表空间中。
B.段
表空间由各个段组成。常见的段有数据段、索引段、回滚段等。InnoDB存储引擎是索引组织表,因此数据及索引,索引即数据。数据段即为B+树的叶子点(leaf node segment),索引段为B+数据的非索引节点(non-leaf node segment)。回滚段比较特殊以后在介绍。段都是引擎自身管理的。
C.区
区是由连续页组成的空间。InnoDB存储引擎页的大小为16KB,一个区有64个连续的页组成,所以每个区的大小都是1MB。InnoDB 1.0.x版本开始引入压缩页,即每个页的大小可以在建表时通过参数key_block_size设置为2K、4K、8K,因此每个区对于页的数量就为512、256、126。InnoDB 1.2.X版本新增参数innodb_page_size将默认页的大小设置为4K、8K,但是页中的数据不是压缩,这是其中的数量同样为256、128。一句话,不论页的大小怎么变化,区的大小不变1M。但是有这样一个问题:在开启独立表空间之后,创建的表默认大小是96K,区中是64个连续的页,创建的表空间应该是1M才对呀?这是因为在每个段的开始时,先用32个页大小的碎片页(fragment page)来保存数据,在使用完这些页之后才是64个连续的页的申请。这样做是对于一些小表或者undo这类的段,可以在开始时申请较少的空间,节省磁盘容量的开销。
D.页
页是InnoDB磁盘管理的最小单位。默认大小为16K,可以通过innodb_page_size将页的大小设置为4K、8K、16K,则所有表中页的大小都为设置值,不可以对其再次修改。除非通过mysqldump导入和导出操作来产生新的库。常见的页的类型有:数据页(B-tree Node)、undo页(unod Log Page)、系统页(System Page)、事务数据页(Transaction system Page)、插入缓冲空闲列表页(Insert Buffer Free List)、未压缩的二进制大对象页(Uncompressed BLOB Page)、压缩的二进制对象页(compressed BLOB Page)
E.行
InnoDB存储引擎是面向行的(row-oriented),也就是说数据是按行进行存放的。每个页存放的行记录也是有硬性定义的,最多运行存放16K/2-200行的记录,即7992行记录。
3.InnoDB行记录格式
InnoDB存储引擎的记录是以行的形式存储的,这就表明页中保存着表中一行行的数据。其类型有REDUNDANT、 COMPACT、COMPRESS、DYNAMIC四种。可以通过show table status;来查看当前库中每张表的数据格式如下图:
A.COMPACT
在MySQL 5.0中引入,其设计目标是高兴的存储数据。也就是一个页中存放的行数据越多,其性能越高。compact行记录的存放方式:
有图可知,compact行记录格式的首部是一个非NULL变长字段长度列表,并且其是按照列的顺序逆序放置的,其长度为:
若列的长度小于255个字节,用1字节表示;
若大于255个字节,用2字节表示。
变长字段之后的第二个部分是NULL标志位,该位置指示了该行数据中是否有NULL值,有这用1表示。该部分占1个字节。接下来就是占用5个字节的记录头信息(record header),每位的含义如下图:
最后的部分就是实际存储每列的数据。注意NULL不占该部分的任何空间,也就是说NULL除了占有NULL标志位,实际存储不占任何空间。每行除了用户定义的列外,还有2个隐藏列(事务ID列(6字节)和回滚指针列(7字节))。若InnoDB表没有定义主键,还会增加一个6字节的rowid列。不管是char类型还是varchar类型,在compact格式下NULL值都不占用任何存储空间。
COMPACT行格式特点:
每个也记录都有5字节长度域,用于指向下一个连续的页,并且可用于实现行锁
有个变长域,存储页中有多少可能为NULL的字段数,长度为 CEILING(N/8)字节,同时也需要用1~2字节存储边长字段所需长度
所有非NULL的变长字段,用1~2字节存储其长度信息
行记录头部域之后,紧跟着就是所有非NULL的字段
UTF8字符集的CHAR(N)字段,优先删除末尾空格,尝试用N字节存储,并且再预留N个字节用于后续数据更新,避免产生碎片
B.REDUNDANT
redundant是MySQL 5.0版本之前InnoDB的行记录存储方式。如图:
redundant行记录格式的首部是一个字段长度偏移列表,同样是按照列的顺序逆序放置的。若列的长度小于255字节,用1字节表示;若大于255字节,用2字节表示。第二个部分记录头信息(record header)。如下图:
最后的部分就是时间存储的每个列的数据。
REDUNDANT行格式特点:
每个页包含6字节ROWID,用于指向下一个连续的页,并且可用于实现行锁
聚集索引中包含所有字段,并且包含额外的6字节TRX_ID,和7字节ROLL_PTR
如果没显式主键,则使用隐含的6字节ROWID作为主键
行记录中存储指向全部字段的指针,字段长度小于128字节时,该指针占用1字节,否则需要2字节
类似CHAR的定长类型字段,也采用固定宽度存储,并且不删除末尾的空格
类似VARCHAR变长类型字段中NULL不会占用实际存储空间,而CHAR定长类型字段中,NULL则占用相应的字节数
C.行溢出数据
InnoDB存储引擎可以将一条记录中的某些数据存储在正在的数据页之外。一般认为BLOB、LOB这类的大对象类型的存储会把数据存放在数据页面之外。这个理解有点偏差,BLOB等可以不将数据房子溢出页面,而且即使是varchar列数据类型依然有可能被存放为行溢出数据。这个主要取决于实际存放的数据。
D.COMPRESS和DYNAMIC
innodb 1.0.x版本引入了新的文件格式(file format),以前支持的compact和redundant格式称为Antelope文件格式,而新的文件格式称为Barracuda文件格式。其Barracuda包括两种新的行记录格式:Compressd和Dynamic。新的两种记录格式对呀存放在BLOB中的数据采用完全的行溢出的方式,在数据页中只存放20个字节的指针,时间的数据存放在Off Page中。而之前的Compact和Redundant会存放钱768个前缀字节
COMPACT与REDUNDANT的区别
COMPACT相比REDUNDANT约可节省20%左右, COMPRESS相比COMPACT约可节省50%左右,但会导致CPU消耗增加,TPS可能只有原来的10%
类似CHAR的定长类型,也采用固定宽度存储,并且不删除末尾的空格(VARCHAR则会删除空格)
所有辅助索引存储了那些在主键定义中存在,但不在辅助索引中存在字段(提高辅助索引检索效率)
DYNAMIC、COMPRESSED格式中,长字段只存储20字节,其余全部off-page(实际长度40字节以内的,则不会发生off-page,哪怕是text/blob)
未完待续。。。