MySQL InnoDB存储引擎(五):表空间
2016 年 11 月 05 日
mysql

    InnoDB把数据保存在表空间内,本质上是一个由一个或多个磁盘文件组成的虚拟文件系统。InnoDB用表空间并不只是存储索引,还保存了回滚段双写缓冲区等。本文将介绍InnoDB表空间的相关性质。

  • 调整InnoDB系统表空间大小

  • 增加InnoDB系统表空间大小

  • 增加InnoDB系统表空间大小的最简单方法就是初始化配置为自动扩展(autoextend)。 在表空间定义中为最后一个数据文件指定autoextend属性,这样InnoDB在空间不足时以64MB的增量自动增加该文件的大小。 可以通过设置innodb_autoextend_increment系统变量的值来更改增量大小(单位为MB)。为了扩展系统表空间,开发人员可通过添加新的数据文件:

    关闭数据库;
    若之前使用了autoextend,可以将其替换为明确的数据文件名及文件大小,如:
    innodb_data_home_dir =
    # before
    innodb_data_file_path = /ibdata/ibdata1:988M:autoextend
    # after
    innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
    		
    启动数据库。
  • 减小InnoDB系统表空间大小

  • InnoDB中,开发人员并不能直接从系统表空间中删除数据文件,为了减小系统表空间,可以执行如下操作:

    使用mysqldump备份所有InnoDB表数据;
    关闭数据库;
    删除所有表空间文件(*.ibd文件),包括ibdataib_log等文件,再删除所有InnoDB表文件(*.frm文件);
    配置新的表空间,启动数据库,再导入之前dump的文件。
  • 改变重建日志文件(Redo Log)的数量及大小

  • 若开发人员想改变InnoDB重建日志文件的数量及大小,可以执行如下操作:

    正常关闭数据库;
    修改my.cnf文件,配置innodb_log_files_in_group(修改文件数量,文件名通常为ib_logfile0, ib_logfile1, ib_logifleN)和(文件大小,若之前有日志文件大小不一致,会先写Checkpoint,再创建新的日志文件);
    启动数据库。
  • 使用裸磁盘分区

  • 开发人员可以在InnoDB系统表空间中使用裸磁盘分区作为数据文件。 此技术在Windows和某些Linux和Unix系统上会启用无缓冲I/O,这样无需文件系统开销,然而最终是否使用裸磁盘分区,还是应该进行测试,以验证此更改是否真实提高了系统性能。对于Linux或Unix系统,可按如下操作执行:

    确保启动MySQL服务器的用户对裸磁盘分区具有读写权限
    创建新数据文件时,须在innodb_data_file_path选项的数据文件大小后指定关键字newraw, 分区必须至少比指定的大小大。注意,InnoDB中的1MB是1024×1024字节,而磁盘规格中的1MB通常意味着1,000,000字节:
    [mysqld]
    innodb_data_home_dir=
    innodb_data_file_path=/dev/hdd1:3Gnewraw;/dev/hdd2:2Gnewraw
    		
    重新启动服务器。InnoDB会根据newraw关键字并初始化新分区。然而,此时不应该创建或更改任何InnoDB表,否则,当下次重新启动服务器时,InnoDB将重新初始化分区,之前的更改将丢失(因此,为了安全起见,InnoDB会组织用户修改newraw分区);
    InnoDB初始化分区后,则关闭数据库,并修改newraw关键字为raw
    [mysqld]
    innodb_data_home_dir=
    innodb_data_file_path=/dev/hdd1:3Graw;/dev/hdd2:2Graw
    		
    重启数据库,此时便可以进行修改操作。
  • Innodb File-Per-Table 表空间

  • 过去,所有InnoDB表和索引都存储在系统表空间中,这种整体方法针对的是完全专用于数据库处理的机器,并且经过精心规划的数据增长,分配给MySQL的任何磁盘存储都不需要用于其他目的。现在InnoDB针对每个表的表空间提供了一个更灵活的选择,将每个InnoDB表及其索引存储在单独的.ibd数据文件中,每个这样的.ibd数据文件都表示一个单独的表空间。此功能可由innodb_file_per_table配置选项控制,默认情况下,此选项在MySQL 5.6.6及更高版本中启用。innodb_file_per_table带来了诸多好处,但也会有一些缺点,下面将分别讲述。

  • File-Per-Table表空间的优势

  • 当截取(TRUNCATE)或删除(DROP)表可以回收对应的磁盘空间。相对于截取或删除存储在系统表空间中的表,其空间只能被InnoDB使用,而不能由操作系统使用。类似地,对驻留在系统表空间中的表执行表复制ALTER TABLE操作可以增加表空间使用的空间量。这样的操作可能需要与表中的数据和索引一样多的额外空间。表复制ALTER TABLE操作所需的额外空间不会释放回操作系统,因为它是针对单个表的文件表空间;
    TRUNCATE TABLE操作时比系统表空间性能更高;
    为了提升I/O性能空间管理或者备份,用户可以将指定表存储在单独的存储设备上。 在以前的版本中,必须将整个数据库目录移动到其他驱动器,并在MySQL数据目录中创建符号链接。在MySQL 5.6.6之后,可以使用语法CREATE TABLE ... DATA DIRECTORY = absolute_path_to_directory指定每个表的位置,即为每个表空间创建外部数据目录
    用户可以执行OPTIMIZE TABLE压缩重新创建每个表的文件表空间。当运行OPTIMIZE TABLE时,InnoDB将使用临时名称创建一个新的.ibd文件,这会使用存储实际数据所需的空间。 当优化完成后,InnoDB将删除旧的.ibd文件,并用新的.ibd文件替换它。如果旧的.ibd文件显着增长,但实际数据仅占其大小的一部分,则运行OPTIMIZE TABLE可以回收未使用的空间;
    用户可以移动单个Innodb表而不是整个数据库;
    用户可以从一个MySQL实例复制单个Innodb表到另一个实例(基于可传输的表空间(transportable tablespace)特性);
    以文件表空间创建的表会使用Barracuda文件格式,Barracuda文件格式支持压缩和动态行格式等特性;
    用户可以使用动态行格式BLOBTEXT列的表启用更高效的存储;
    发生损坏服务器无法重新启动,或者备份和二进制日志不可用时,File-per-table表空间可以提升成功恢复的机会节省时间
    File-per-table表空间便于在复制备份表时,进行每个表的状态报告;
    用户无需访问MySQL,便可以在文件系统级别监控表大小;
    innodb_flush_method设置为O_DIRECT时,通用Linux文件系统不允许并发写入单个文件。因此,将file-per-table表空间与innodb_flush_method结合使用,有可能会提高并发写性能。
    系统表空间存储数据字典和撤销日志,并且大小受InnoDB表空间大小的限制,对于file-per-table表空间,每个表都有自己的表空间,这就提供了增长空间。
  • File-Per-Table表空间的潜在缺点

  • 对于file-per-table表空间,每个表可能都有未使用的空间,并且只能由相同表的行使用,如果未能正确管理,这可能会导致浪费空间;
    fsync操作必须在每个打开的表上运行,而不是在单个文件上运行。 因为每个文件都有单独的fsync操作,因此多个表上的写操作不能合并到单个I/O操作中,这可能需要InnoDB执行更多的fsync操作;
    mysqld必须为每个表保留一个打开的文件句柄,如果在file-per-table表空间中有许多表,这可能会影响性能;
    会使用更多的文件描述符;
    innodb_file_per_table默认情况下在MySQL 5.6.6及更高版本中启用。如果考虑向后兼容MySQL 5.5或5.1,可以考虑禁用它。在ALTER TABLE重新创建表(ALGORITHM = COPY)的情况下,禁用innodb_file_per_table可防止ALTER TABLE将InnoDB表从系统表空间移动到单个.ibd文件;
    如果许多表都在增长,则由可能产生更多碎片,这可能阻碍DROP TABLE表扫描性能。 不过,当碎片可控时,在innodb_file_per_table表空间则可以提高性能;
    当删除innodb_file_per_table表空间时,将扫描缓冲池,对于大小为数十GB的缓冲池,可能需要几秒钟。 使用内部锁执行扫描,则可能会延迟其他操作,而系统表空间中的表不受影响;
    innodb_autoextend_increment变量定义了在自动扩展共享表空间时,扩展的增量大小(以MB为单位),但不适用于自动扩展的file-per-table表空间文件,无论innodb_autoextend_increment是否设置,初始扩展是比较小的,之后扩展则以4MB的增量大小执行。
  • 启用或禁用File-Per-Table表空间

  • 启用或禁用,只需配置innodb_file_per_table,如:

    [mysqld]
    innodb_file_per_table=1
    	

    或者运行时修改:

    SET GLOBAL innodb_file_per_table=1;
    	

    具体细节可见这里

  • 为File-Per-Table创建外部数据目录

  • File-Per-Table创建外部数据目比较简单,只需在创建表时指定外部目录即可,这样InnoDB将在对应目录创建目标数据库目录及表空间文件,如:

    CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/path/to/data_dir';
    	
  • 复制File-Per-Table表空间到其他数据库实例

  • 将文件每表表空间从一个数据库实例复制到另一个数据库实例,也称为可传输表空间功能。在MySQL 5.7.4之前,只支持非分区的InnoDB表。从MySQL 5.7.4开始,还支持分区的InnoDB表和各个InnoDB表分区和子分区。现实中,可能会有不同原因需要作表复制,如:

    作数据报表时,为了不影响生产服务器上,需要先复制到临时数据库;
    为了在从服务器上建立相同的表数据;
    在出现问题或错误后,恢复表或分区的备份版本;
    作为一种更快的移动数据的方式,而不是通过mysqldump命令倒入,这样数据可以立即可用,不必重新插入并重建索引;
    File-Per-Table表空间移动到具有更适合系统要求的存储介质的服务器;
    ...

    对于具体如何执行表复制,可参考该文章

  • 在单独的表空间中存储撤销日志(Undo Logs)

  • 用户可以将InnoDB撤销日志(也被称为回滚段)存储在系统表空间之外的一个或多个单独的撤销表空间中。 此设计与默认配置不同,其中撤销日志是系统表空间的一部分。 撤消日志的I/O模式使得这些表空间迁移到SSD存储更具优势,同时将系统表空间保留在硬盘存储上。 用户不能删除创建的用于单独存储撤销日志的表空间,也不能删除这些表空间中的各个段。但是,从MySQL 5.7.5开始,存储在撤销表空间中的撤销日志可以被截取(truncate)。截取撤销日志细节可参考这里。由于撤销日志相关特性不能动态配置,因此只能在初始化MySQL实例时启用。

    撤销日志保存在单独的文件中,这样使得与事务数据相关的I/O和内存操作得到优化。 例如,因为撤销日志被写入磁盘,但其实很少会读取(仅在崩溃恢复的情况下),它不需要保存在文件系统高速缓存中,从而允许将更多的内存用于InnoDB缓冲区

    启用撤销日志特性,大致步骤如下,细节可参考这里

    配置保存撤消日志的路径,可以在MySQL配置文件或启动脚本中配置innodb_undo_directory参数, 如果没有指定,则在MySQL数据目录中创建撤销表空间
    配置innodb_undo_logs选项的起始值,可以从相对较低的值开始,并随时间增加,以监控对性能的影响;
    配置innodb_undo_tablespaces,指定撤销表空间的数量;
    根据上述配置,创建新的MySQL实例(初始化启动MySQL);
    对I/O性能作基准测试;
    定期增加innodb_undo_logs的值并重新作性能测试,找出I/O性能最高时的innodb_undo_logs值。
  • 截取撤销日志(Truncating Undo Logs)

  • 从MySQL5.7.5后,用户可以通过innodb_undo_log_truncate配置选项开启截取撤销日志。当启用innodb_undo_log_truncate后,超过由innodb_max_undo_log_size配置定义的阈值的表空间将标记为截断。注意,只有存储在撤销表空间中的撤销日志才能被截取,系统表空间中的撤销日志不支持截断。 要使得截取发生,必须至少有两个撤销表空间,且都启用了重坐撤销日志的配置选项。 这也意味着innodb_undo_tablespaces必须设置为大于等于2的值,innodb_undo_logs必须设置为等于或大于35的值。具体细节可参考这里

  • InnoDB通用表空间(General Tablespaces)

  • 通用表空间是一种新的InnoDB表空间,在MySQL 5.7.6中引入。通用表空间特性主要有以下功能:

    系统表空间类似,通用表空间是可以存储多个表的数据;
    通用表空间相对于file-per-table表空间具有潜在的内存优势。服务器在表空间的生命周期内会将表空间元数据保存在内存中,而将多张表的元数据信息保存在通用表空间内相对于每张表单独将各自元数据保存在自己的表空间内会更节省内存;
    通用表空间数据文件可以被放置在相对于或独立于MySQL数据目录的目录中,该目录提供了许多对file-per-table表空间的数据文件和存储管理的功能。与file-per-table表空间表空间一样,将数据文件放在MySQL数据目录之外,使得可以分别管理关键表的性能,例如为特定表设置DRBD,或将表绑定到特定磁盘;
    通用表空间支持AntelopeBarracuda文件格式,因此支持所有行格式及相关特性。通过支持这两种文件格式,通用表空间不依赖于innodb_file_format或>innodb_file_per_table设置,这些变量对通用表空间没有任何影响;
    TABLESPACE选项可与CREATE TABLE一起,在通用表空间file-per-table表空间或系统表空间中创建表;
    TABLESPACE选项可与ALTER TABLE一起,在通用表空间file-per-table表空间或系统表空间之间移动表。以前,不可能将表从file-per-table表空间移动到系统表空间,而使用通用表空间特性,则可以这么做。

    通用表空间具体使用,可参考这里

  • InnoDB表空间加密(Encryption)

  • InnoDB支持对存储在file-per-table表空间中的表的数据进行加密,该特性对物理表空间的数据文件提供静态加密。InnoDB表空间加密使用两层加密密钥体系结构,包括主加密密钥表空间密钥。当InnoDB表被加密时,表空间密钥被加密并存储在表空间头中。 当应用程序或已认证的用户想要访问加密的表空间数据时,InnoDB使用主加密密钥来解密表空间密钥。表空间密钥的解密版本从不改变,但是主加密密钥可以根据需要改变,此操作称为主密钥旋转。上述加密特性需依赖keyring_file插件,具体加密细节可参考这里

  • 总结

  • 以上,则是InnoDB表空间的相关特性,开发人员则可以针对不同业务特性,场景等作一系列相关优化等。

  • 参考文献

好人,一生平安。