14.12 InnoDB 磁盘I/O 和文件空间管理

2020-05-11 00:00:00 文件 空间 页面 截断 长度

参考官方文档:

dev.mysql.com/doc/refma


14.12.1 InnoDB 磁盘I/O


InnoDB 通过创建许多线程来处理I / O操作,同时在I/O仍进行时允许其他数据库操作,来尽可能的使用异步磁盘I/O,


在Linux和Windows平台上,InnoDB使用可用的OS和库函数来执行“本机”异步I / O. 在其他平台上,InnoDB仍然使用I / O线程,但线程实际上可能等待I / O请求完成; 这种技术称为“近似的”异步I / O.


预读

如果InnoDB可以确定很快就需要数据,它会执行预读操作以将该数据放入缓冲池,以便在内存中可用。 对连续数据进行一些大的读取请求比一些小的,分离的请求更有效。 InnoDB中有两个预读的方式:


  • 在顺序预读中,如果InnoDB注意到表空间中某个段的访问模式是顺序的,则它会预先将数据库页的一批读取发送到I/O系统。
  • 在随机预读中,如果InnoDB注意到表空间中的某些区域似乎正在被完整的读入缓冲池,则会将剩余的读取发布到I/O系统。


双写缓存


InnoDB使用一种新颖的文件刷新技术,涉及一种称为doublewrite缓存的结构,在大多数情况下默认启用它(innodb_doublewrite = ON)。 它可以在崩溃或停电后为恢复增加安全性,并通过减少对fsync()操作的需求来提高大多数Unix的性能。

在写页面到数据文件之前,InnoDB首先将它们写入到一个称为doublewrite buffer 的连续表空间区域。只有在对doublewrite 缓存的写入和刷新完成后,InnoDB才会将页面写入数据文件中的正确位置。 如果在页面写入过程中存在操作系统,存储子系统或mysqld进程崩溃(导致页面错误条件),InnoDB稍后可以在恢复期间从doublewrite缓冲区中找到页面的良好副本。

如果系统表空间文件(“ibdata文件”)位于支持原子写入的Fusion-io设备上,则会自动禁用双写缓冲,并且Fusion-io原子写入将用于所有数据文件。 由于双写缓冲区设置是全局的,因此对于驻留在非Fusion-io硬件上的数据文件,也禁用双写缓冲。 此功能仅在Fusion-io硬件上受支持,仅适用于Linux上的Fusion-io NVMFS。 要充分利用此功能,建议使用innodb_flush_method设置O_DIRECT。



14.12.2 文件空间管理


使用innodb_data_file_path配置选项在配置文件中定义的数据文件构成InnoDB系统表空间。 这些文件在逻辑上连接在一起以形成系统表空间。 没有使用条带化。 您无法在系统表空间中定义表的分配位置。 在新创建的系统表空间中,InnoDB从个数据文件开始分配空间。

为了避免在系统表空间中存储所有表和索引所带来的问题,可以启用innodb_file_per_table配置选项(默认值),该选项将每个新创建的表存储在单独的表空间文件中(扩展名为.ibd)。 对于以这种方式存储的表,磁盘文件中的碎片较少,并且当表被截断时,空间将返回到操作系统,而不保留在InnoDB在系统表空间中。


您还可以在一般表空间中存储表。 常规表空间是使用CREATE TABLESPACE语法创建的共享表空间。 它们可以在MySQL数据目录之外创建,能够保存多个表,并支持表所有的行格式。


页,区,段和表空间


每个表空间都包含数据库页面。 MySQL实例中的每个表空间都具有相同的页面大小。 默认情况下,所有表空间的页面大小均为16KB; 通过在创建MySQL实例时指定innodb_page_size选项,可以将页面大小减小到8KB或4KB。 您还可以将页面大小增加到32KB或64KB。 有关更多信息,请参阅innodb_page_size文档。

--这里 页面类似于oracle block。

对于大16KB的页面(64个连续的16KB页面,或128个8KB页面,或256个4KB页面),页面被分组为大小为1MB的区。 对于32KB的页面大小,扩展区大小为2MB。 对于64KB的页面大小,扩展区大小为4MB。 表空间内的“文件”在InnoDB中称为段。 (这些段与回滚段不同,后者实际上包含许多表空间段。)

--类似oracle 中段区块


当一个段在表空间内增长时,InnoDB一次一个地分配前32个页面。 之后,InnoDB开始将整个区分配给该段。 InnoDB一次可以向一个大段添加多4个扩展区,以确保数据的良好顺序性。

在InnoDB中为每个索引分配了两个段。 一个用于B树的非叶节点,另一个用于叶节点。 保持叶子节点在磁盘上连续可以实现更好的顺序I / O操作,因为这些叶节点包含实际的表数据。

表空间中的某些页面包含其他页面的位图,因此InnoDB表空间中的一些区不能作为整体分配给段,而只能作为单独的页面。


当您通过发出SHOW TABLE STATUS语句请求表空间中的可用空间时,InnoDB会报告表空间中空闲的扩展区。 InnoDB总是为清理和其他内部目的保留一些区; 这些保留的区不包含在可用空间中。


从表中删除数据时,InnoDB会收缩相应的B树索引。 释放的空间是否可供其他用户使用取决于删除模式是否释放单个页面或区到表空间。 删除表或从中删除所有行都可以保证将空间释放给其他用户,但请记住,只有清除操作才会删除已删除的行,这在事务回滚或一致性读取不再需要时会自动发生。 (参见第14.3节“InnoDB多版本”。)



页面如何关联到表的行


对于4KB,8KB,16KB和32KB innodb_page_size设置,大行长度略小于数据库页面的一半。 例如,对于默认的16KB InnoDB页面大小,大行长度略小于8KB。 对于32KB页面,大行长度略小于16KB。

如果一行没有超过大行长度,则所有行都存储在页面内。 如果行超过大行长度,则选择可变长度列用于外部页外存储,直到该行符合大行长度限制。 可变长度列的外部页外存储因行格式而异:


  • COMPACT 和 REDUNDANT 行格式

当为外部页外存储选择可变长度列时,InnoDB在行中本地存储前768个字节,其余外部存储在溢出页中。 每个这样的列都有自己的溢出页列表。 768字节的前缀伴随着一个20字节的值,该值存储列的真实长度并指向溢出列表,其中存储了值的其余部分。 请参见第14.11.4节“COMPACT和REDUNDANT行格式”。


  • DYNAMIC 和 COMPRESSED 行格式

当为外部页外存储选择可变长度列时,InnoDB在行中本地存储一个20字节的指针,其余部分存储在溢出页面中。 请参见第14.11.3节“动态和压缩行格式”。


LONGBLOB和LONGTEXT列必须小于4GB,并且总行长度(包括BLOB和TEXT列)必须小于4GB。



14.12.3 InnoDB 检查点


使日志文件非常大可能会减少检查点期间的磁盘I / O. 将日志文件的总大小设置为与缓冲池一样大或更大是有意义的。 虽然在过去大型日志文件可能会使崩溃恢复花费过多时间,但从MySQL 5.5开始,崩溃恢复的性能增强使得在崩溃后快速启动时可以使用大型日志文件。 (在MySQL 5.5中,默认的InnoDB存储引擎可以使用这种改进。)



checkpoint 如何工作


InnoDB实现了一种称为 fuzzy checkpointing 的检查点机制。 InnoDB以小批量的方式从缓冲池中刷新已修改的数据库页面。 无需在一个批处理中刷新缓冲池,这会在检查点过程中中断用户SQL语句的处理。

在崩溃恢复期间,InnoDB会查找写入日志文件的检查点标签。 它知道在标签之前对数据库的所有修改都存在于数据库的磁盘映像中。 然后,InnoDB从检查点向前扫描日志文件,将记录的修改应用于数据库。



14.12.4 表的碎片整理


从二级索引中随机插入或删除可能会导致索引碎片化。 碎片意味着磁盘上索引页的物理排序不接近页面上记录的索引排序,或者在64-页块中有许多未使用的页面被分配给索引。

碎片化的一个症状是表占用的空间比“应该”占用的空间多。 多少确切,很难确定。 所有InnoDB数据和索引都存储在B树中,其填充因子可能在50%到100%之间变化。 碎片的另一个症状是像这样的表扫描需要比“应该”花费更多的时间:

SELECT COUNT(*) FROM t WHERE non_indexed_column <> 12345;


前面的查询要求MySQL执行全表扫描,这是对大表的慢类型的查询。

要加速索引扫描,您可以定期执行“null”ALTER TABLE操作,这会导致MySQL重建表:

ALTER TABLE tbl_name ENGINE=INNODB


您还可以使用ALTER TABLE tbl_name FORCE执行重建表的“null”更改操作。


ALTER TABLE tbl_name ENGINE = INNODB和ALTER TABLE tbl_name FORCE都使用在线DDL。


执行碎片整理操作的另一种方法是使用mysqldump将表转储到文本文件,删除表,然后从转储文件重新加载它。


如果索引中的插入总是升序并且记录仅从末尾删除,则InnoDB文件空间管理算法可确保不会发生索引中的碎片。


14.12.5 使用TRUNCATE TABLE回收磁盘空间

要在截断InnoDB表时回收操作系统磁盘空间,该表必须存储在其自己的.ibd文件中。 对于要存储在其自己的.ibd文件中的表,必须在创建表时启用innodb_file_per_table。 此外,被截断的表与其他表之间不能存在外键约束,否则TRUNCATE TABLE操作将失败。 但是,允许同一表中两列之间的外键约束。


截断表时,将删除该表并在新的.ibd文件中重新创建,并将释放的空间返回给操作系统。 这与截断存储在InnoDB系统表空间(innodb_file_per_table = OFF时创建的表)和存储在共享通用表空间中的表的InnoDB表形成对比,只有InnoDB可以在截断表后使用被释放的空间。


截断表并将磁盘空间返回给操作系统的能力也意味着物理备份可以更小。 截断存储在系统表空间(innodb_file_per_table = OFF时创建的表)或通用表空间中存储的表会在表空间中留下未使用的块。

相关文章