15.2.13.1. 索引的物理结构

15.2.13.2. 缓冲插入

15.2.13.3. 适应的哈希索引

15.2.13.4. 物理记录结构

MySQL为表把它的数据词典信息以.frm文件的形式存在数据库目录里,这对所有MySQL存储引擎都是真的。但是每个InnoDB表在表空间内的InnoDB内部数据词典里有它自己的条目。当MySQL移除表或数据库,它不得不删除.frm文件和InnoDB数据词典内的相应条目。这就是为什么你不能在数据库之间简单地移动.frm文件来移动InnoDB表。

每个InnoDB表有专门索引,被称为clustered index,对行的数据被存于其中。如果你对你的表定义一个PRIMARY KEY, 主键的索引是集束索引。

如果你没有为表定义PRIMARY KEY,MySQL拾取第一个仅有NOT NULL列的UNIQUE索引作为主键,并且InnoDB把它当作集束索引来用。如果表中没有这样一个索引,InnoDB内部产生一个集束索引,其中用InnoDB在这样一个表内指定给行的行ID来排序行。行ID是一个6字节的域,它在新行被插入的时候单一地增加。因此被行ID排序的行是物理地按照插入顺序排的。

通过集束索引访问一个行是较快的,因为行数据是在索引搜索引导的同一页面。如果表是巨大的,当对比于传统解决方案,集束索引构架经常节约磁盘I/O。(在许多数据库,数据传统地被存在与索引记录不同的页)。

在InnoDB中,非集束索引里的记录(也称为第二索引)包含对行的主键值。InnoDB用这个 主键值来从集束索引中搜索行。注意,如果主键是长的,第二索引使用更多空间。

InnoDB比较CHAR和VARCHAR字符串不同长度,以便在较短字符串中剩下的长度被处理视为用空格补上的。

15.2.13.1. 索引的物理结构

所有InnoDB的索引是B数,其中索引记录被存储在树的树叶页。一个索引页的默认大小是16KB。当新记录被插入,InnoDB试着为将来索引记录的插入和更新留下十六分之一的空白页。

如果索引记录以连续的顺序被插入(升序或者降序),结果索引页大约是15/16满。如果记录被以随机的顺序被插入,页面是从1/2到 15/16满。如果索引页的填充因子降到低于1/2,InnoDB试着搜索索引树来释放页。

15.2.13.2. 缓冲插入

在数据库应用中,主键是一个唯一的识别符,并且新行被以主键的升序来插入,这是个常见的情况。因此,到集束索引的插入不需要从一个磁盘随机读。

另一方面,第二索引通常是非唯一的,到第二索引的插入以相对随机次序发生。这可能会导致大量的随机磁盘I/O操作,而没有一个被用在InnoDB中的专用机制。

如果一个索引记录应该被插入到一个非唯一第二索引,InnoDB检查第二索引页是否在缓冲池中。如果是,InnoDB直接插入到索引页。如果索引页没有在缓冲池中被发现,InnoDB插入记录到一个专门的插入缓冲结构。插入缓冲被保持得如此小以至于它完全适合在缓冲池,并且可以非常快地做插入。

插入缓冲周期地被合并到数据库中第二索引树里。把数个插入合并到索引树的同一页,节省磁盘I/O操作,经常地这是有可能的。据测量,插入缓冲可以提高到表的插入速度达15倍。

在插入事务被提交之后,插入缓冲合并可能连续发生。实际上,服务器关闭和重启之后,这会连续发生。(请参阅15.2.8.1节,“强制恢复”)。

当许多第二索引必须被更新之时,并且许多行已被插入之时,插入缓冲合并可能需要数个小时。在这个时间内,磁盘I/O将会增加,这样会导致磁盘绑定查询明显缓慢。另一个明显的后台I/O操作是净化线程(请参阅15.2.12节,“实现多版本化”)。

15.2.13.3. 适应的哈希索引

如果一个表几乎完全配合主内存,在其上执行查询最快的方法就是使用哈希索引。InnoDB有一个自动机制,它监视对为一个表定义的索引的索引搜索。如果InnoDB注意到查询会从建立一个哈希索引中获益,它会自动地这么做。

注意,哈希索引总是基于表上已存在的B树索引来建立。根据InnoDB对B树索引观察的搜索方式,InnoDB会在为该B树定义的任何长度的 键的一个前缀上建立哈希索引。 哈希索引可以是部分的:它不要求整个B树索引被缓存在缓冲池。InnoDB根据需要对被经常访问的索引的那些页面建立哈希索引。

在某种意义上,InnoDB通过针对丰富主内存的适应的哈希索引机制来剪裁自己,更加靠近主内存数据库的架构。

15.2.13.4. 物理记录结构

InnoDB表中的记录有如下特征:

·         InnoDB中每个索引记录包含一个6字节的头。这个头被用来将连续的记录连接在一起,并且也用在row-level锁定中。

·         集束索引中的记录包含对所有自定义列的域。此外,有一个6字节的域给事务ID以及一个7字节的域给滚动指针。

·         如果没有为一个表定义初级键,每个集束索引记录也包含一个6字节的行ID域。

·         每个第二索引记录也包含为集束索引键定义的所有域。

·         一个记录也包含一个指向该记录每一个域的指针,如果在一个记录中域的总长度小于128字节,该指针时一个字节;否则就是2字节。这些指针的阵列被称为记录目录。这些指针指向的区域被称为记录的数据部分。

·         内部地,InnoDB以固定长度格式存储固定长度字符列,比如CHAR(10)。InnoDB从VARCHAR列截短跟踪空间。注意,MySQL可以内部地把CHAR列转换为VARCHAR列。请参阅13.1.5.1节,“安静的列规格改变”

·         一个SQL的NULL值在记录目录里保留1或2字节。此外,SQL的NULL值如果被保存在可变长度列,则在记录数据部分保留零字节。在一个固定长度列,它在记录的数据部分保留该列的固定长度。为NULL值保留固定空间背后的动机是之后该 列从NULL值到非NULL值的更新可以就地完成,且不会导致索引页的碎片。