15.4 InnoDB Architecture
InnoDB 存储引擎体系结构的主要组件。
15.4.1 Buffer Pool
15.4.2 Change Buffer
15.4.3 Adaptive Hash Index
15.4.4 Redo Log Buffer
15.4.5 System Tablespace
15.4.6 Doublewrite Buffer
15.4.7 Undo Logs
15.4.8 File-Per-Table Tablespaces
15.4.9 General Tablespaces
15.4.10 Undo Tablespace
15.4.11 Temporary Tablespace
15.4.12 Redo Log
15.4.1 Buffer Pool
缓冲池是内存中的一个区域,InnoDB 将表和索引数据作为数据进行缓存。缓冲池允许经常使用的数据直接从内存中处理,这加快了处理速度。在专用数据库服务器上,多达 80% 的物理内存通常分配给 InnoDB 缓冲池。
为了提高大容量读取操作的效率,缓冲池被划分为可能包含多个行的页面。为了提高缓存管理的效率,缓冲池被实现为一个页面链表,使用的是 LRU 算法的变体,很少使用的数据会在缓存中老化。
15.4.2 Change Buffer
更改缓冲区是一种特殊的数据结构,当受影响的页面不在缓冲池中时,它会缓存对次要索引页的更改。缓存的更改(可能由插入、更新或删除操作 (DML) 导致)在随后页面被其他读取操作加载到缓冲池中时被合并。与聚集索引不同,辅助索引通常是非惟一的,并且插入到辅助索引中的插入顺序是相对随机的。类似地,删除和更新可能会影响不相邻地位于索引树中的次要索引页。当受影响的页面被其他操作读取到缓冲池中时,在稍后的时间合并缓存的更改,可以避免大量随机访问 I/O,而从磁盘读到辅助索引页需要大量随机访问 I/O。
定期地,当系统大部分空闲时或在缓慢关闭期间运行的清除操作将更新的索引页写入磁盘。清除操作可以更有效地为一系列索引值编写磁盘块,而不是立即将每个值写入磁盘。
当有许多次要索引需要更新和许多受影响的行时,更改缓冲区合并可能需要几个小时。在此期间,磁盘 I/O 将增加,这可能会导致磁盘绑定查询的显著放缓。更改缓冲区合并也可能在提交事务之后继续发生。事实上,在服务器关闭和重新启动之后,更改缓冲区合并可能会继续发生。具体的可以参照 Forcing InnoDB Recovery。
在内存中,更改缓冲区占用了 InnoDB 缓冲池的一部分。在磁盘上,更改缓冲区是系统表空间的一部分,因此在数据库重新启动时,索引更改仍然保持缓冲。
在更改缓冲区中缓存的数据类型由 innodb_change_buffering 配置选项控制(Configuring InnoDB Change Buffering)。你还可以配置最大更改缓冲区大小(Configuring the Change Buffer Maximum Size)。
如果索引包含下行索引列,或者主键包含下行索引列,则不支持对二级索引进行更改缓冲。
以下选项可用于更改缓冲区监控:
InnoDB 标准监控器输出包含更改缓冲区的状态信息。要查看监视数据,可以使用 SHOW ENGINE INNODB STATUS \G.
# SHOW ENGINE INNODB STATUS \G
更详细的信息(InnoDB Standard Monitor and Lock Monitor Output
INFORMATION_SCHEMA.INNODB_METRICS 表提供了 InnoDB 标准监视器输出中的大部分数据点,以及其他数据点。要查看更改缓冲区指标和每个指标的描述,可以使用如下的语句:
# SELECT NAME,COMMENTFROM INFORMATION_SCHEMA.INNODB_METRICSWHERE NAME LIKE '%ibuf%'\G
想要了解更多的信息,可以查看 InnoDB INFORMATION_SCHEMA Metrics Table
INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 表提供关于缓冲池中每个页面的元数据,包括更改缓冲区索引和更改缓冲区位图页面。更改缓冲区页面由 PAGE_TYPE 标识。IBUF_INDEX 是更改缓冲区索引页的页面类型,而 IBUF_BITMAP 是更改缓冲区位图页的页面类型。
查询 INNODB_BUFFER_PAGE 表可以引入显著的性能开销。为了避免影响性能,复制您希望在测试实例上调查的问题,并在测试实例上运行查询。
例如,你可以查询 INNODB_BUFFER_PAGE 表,以确定 IBUF_INDEX 和 IBUF_BITMAP 页面的大致数量占总缓冲池页面的百分比。
# SELECT (
SELECT COUNT(*)FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE
WHERE PAGE_TYPE LIKE 'IBUF%') AS change_buffer_pages,
(SELECT COUNT(*)FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE) AS total_pages,
(SELECT ((change_buffer_pages/total_pages)*100)) AS change_buffer_page_percentage;
性能模式为高级性能监视提供了更改缓冲区互斥锁等待工具。要查看更改缓冲插装,可以使用如下的语句:
# SELECT *FROM performance_schema.setup_instruments
WHERE NAME LIKE '%wait/synch/mutex/innodb/ibuf%';
15.4.3 Adaptive Hash Index
自适应散列索引让 InnoDB 更像一个内存数据库,在系统上有适当的工作负载组合和缓冲池的足够内存,而不会牺牲任何事务特性或可靠性。这个特性由 innodb_adaptive_hash_index 选项启用,或者在服务器启动时由 —skip-innodb_adaptive_hash_index 关闭。
基于观察到的搜索模式,MySQL 使用索引键的前缀构建哈希索引。键的前缀可以是任意长度,可能只有b 树中的一些值出现在哈希索引中。哈希索引是基于对经常被访问的索引页的需求而建立的。
如果一个表几乎完全适合主内存,哈希索引可以通过允许直接查找任何元素来加快查询速度,将索引值转换为某种指针。InnoDB 有一个监控索引搜索的机制。如果 InnoDB 注意到查询可以从构建哈希索引中获益,那么它会自动这么做。
对于某些工作负载,哈希索引查找的加速比监视索引查找和维护哈希索引结构的额外工作要重要得多。有时,保护对自适应哈希索引的访问的读/写锁会在繁重的工作负载(如多个并发连接)下成为争用的来源。使用 LIKE 操作符和 % 通配符的查询也不会从 AHI 中获益。对于不需要自适应哈希索引的工作负载,关闭它可以减少不必要的性能开销。因为很难预先预测这个特性是否适合特定的系统,所以考虑使用一个实际的工作负载来运行启用和禁用它的基准测试。MySQL 5.6 和更高的体系结构更改使得与早期版本相比,更适合禁用自适应哈希索引,尽管默认情况下仍然启用它。
对自适应哈希索引搜索系统进行了分区。每个索引都绑定到一个特定的分区上,每个分区都由一个单独的锁存器保护。分区是由参数 innodb_adaptive_hash_index_parts 控制的。该参数默认设置为 8。最大设置为 512。
哈希索引总是基于表上现有的 B-tree 索引构建的。InnoDB 可以在为 b 树定义的键的任何长度的前缀上构建哈希索引,这取决于 InnoDB 对 b 树索引所观察到的搜索模式。哈希索引可以是部分的,只覆盖那些经常被访问的索引页。
你可以在 SHOW ENGINE INNODB 状态命令输出的信号量部分监视自适应哈希索引的使用和使用它的争用。如果你看到在 btr0sea 中创建的一个 rw 锁存器上有许多线程在等待。那么禁用自适应哈希索引可能是有用的。
15.4.4 Redo Log Buffer
重做日志缓冲区是将数据写入重做日志的内存区域。重做日志缓冲区大小由 innodb_log_buffer_size 配置选项定义。重做日志缓冲区定期刷新到磁盘上的日志文件。大型重做日志缓冲区允许大型事务运行,而无需在事务提交之前将重做日志写入磁盘。因此,如果您有更新、插入或删除许多行的事务,使日志缓冲区更大可以保存磁盘 I/O。
innodb_flush_log_at_trx_commit 选项控制如何将重做日志缓冲区的内容写入日志文件。innodb_flush_log_at_timeout 选项控制重做日志刷新频率。
15.4.5 System Tablespace
InnoDB 系统表空间包含 InnoDB 数据字典(用于与 InnoDB 相关的对象的元数据),它是 doublewrite 缓冲区、更改缓冲区和 undo 日志的存储区域。系统表空间还包含在系统表空间中创建的任何用户创建的表的表和索引数据。系统表空间被认为是一个共享表空间,因为它由多个表共享。
系统表空间由一个或多个数据文件表示。默认情况下,在 MySQL 数据目录中创建一个名为 ibdata1 的系统数据文件。系统数据文件的大小和数量由 innodb_data_file_path 启动选项控制。
15.4.6 Doublewrite Buffer
双写缓冲区是位于系统表空间中的一个存储区域,InnoDB 在此空间中编写从 InnoDB 缓冲池中刷新的页面,然后将页面写到数据文件的适当位置。只有在将页面刷新并写入到双写缓冲区之后,InnoDB 才会将页面写入到正确的位置。如果在页面写入过程中存在操作系统、存储子系统或 mysqld 进程崩溃,InnoDB 以后可以在崩溃恢复期间从双写缓冲区找到页面的良好副本。
尽管数据总是被写入两次,但双写缓冲区不需要两倍的 I/O 开销,也不需要两倍的 I/O 操作。数据被写入双写缓冲区本身,作为一个大的顺序块,使用一个 fsync() 调用操作系统。
在大多数情况下,双写缓冲区在默认情况下是启用的。要禁用双写缓冲区,请将 innodb_doublewrite 设置为 0。
如果系统表空间文件(“ibdata文件”)位于支持原子写入的 Fusion-io 设备上,则会自动禁用双写缓冲,并将 Fusion-io 原子写入用于所有数据文件。由于双写缓冲区设置是全局的,所以对于驻留在非 Fusion-io 硬件上的数据文件,双写缓冲区也被禁用。此特性仅支持 Fusion-io 硬件,仅支持Linux上的 Fusion-io NVMFS。为了充分利用这个特性,建议使用 O_DIRECT 的 innodb_flush_method 设置。
15.4.7 Undo Logs
撤销日志是与单个事务相关联的撤销日志记录的集合。撤消日志记录包含关于如何撤消事务对群集索引记录的最新更改的信息。如果另一个事务需要查看原始数据(作为一致读取操作的一部分),则从撤消日志记录中检索未修改的数据。撤销日志存在于撤销日志段中,这些日志段包含在回滚段中。回滚段驻留在撤消表空间和临时表空间中。
临时表空间和每个撤销表空间分别支持最大128个回滚段。innodb_rollback_segment 配置选项定义回滚段的数量。每个回滚段支持最多 1023 个并发数据修改事务。
15.4.8 File-Per-Table Tablespaces
当启用 innodb_file_per_table 选项时,将在每个表的文件表空间中创建表。否则在系统表空间中创建 InnoDB 表。每个表的每个文件表空间都由一个.ibd 数据文件表示,该文件在数据库目录中默认创建。
每个表的文件表空间支持动态和压缩行格式,这些格式支持可变长度数据的页外存储等特性。
15.4.9 General Tablespaces
InnoDB 共享表空间的创建可以使用 CREATE TABLESPACE 来实现,添加一个表到共享表空间可以使用如下的语句。
# CREATE TABLE table_name ... TABLESPACE [=] tablespace_name
# ALTER TABLE table_name TABLESPACE [=] tablespace_name
15.4.10 Undo Tablespace
一个 undo tablespace 由一个或多个包含 undo log 的文件组成。InnoDB 使用的 undo tablespace 的数量由 innodb_undo_tablespaces 配置选项定义的。
15.4.11 Temporary Tablespace
用户创建的 InnoDB 临时表和磁盘上的 InnoDB 临时表会在一个共享的临时表空间中创建。innodb_temp_data_file_path 配置选项定义临时表空间数据文件的相对路径、名称、大小和属性。如果没有为 innodb_temp_data_file_path 指定值,默认会在数据目录下创建一个大小为 12MB,名为 ibtmp1 的自动扩展表。在正常关闭或中止初始化时删除共享的临时表空间,并在每次启动服务器时重新创建。临时表空间在创建时接收动态生成的空间 ID,这将防止与现有空间 ID 发生冲突。如果无法创建临时表空间,则拒绝启动。如果服务器意外停止,则不会删除临时表空间。在这种情况下,数据库管理员可以手动删除临时表空间或重新启动服务器,服务器将删除并重新创建临时表空间。INFORMATION_SCHEMA.FILES 提供了关于 InnoDB 临时表的元数据信息。可以使用如下的语句查看:
# SELECT * FROM INFORMATION_SCHEMA.FILESWHERE TABLESPACE_NAME='innodb_temporary'\G
INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO 提供了关于用户创建的临时表的元数据。
临时表 undo logs 与 redo logs 是不一样的,因为在崩溃恢复期间不会恢复临时表,也不需要重做日志。但是,临时表撤销日志用于服务器运行时的回滚。这种特殊类型的非重做撤销日志通过避免重做临时表和相关对象的日志 I/O 来提高性能。临时表撤消日志驻留在临时表空间中。innodb_rollback_segment 配置选项定义临时表空间使用的回滚段的数量。
15.4.12 Redo Log
redo 日志是在崩溃恢复期间使用的基于磁盘的数据结构,用于纠正不完整事务写入的数据。在正常操作期间,redo 日志编码请求,以更改来自 SQL 语句或低级 API 调用的 InnoDB 表数据。未完成更新数据文件的修改,在初始化过程中以及在连接被接受之前自动重新播放。
默认情况下,redo 日志在磁盘上被物理地表示为一组文件,名为 ib_logfile0 和 ib_logfile1。MySQL 以循环方式写入重做日志文件。重做日志中的数据按照受影响的记录进行编码,这些数据统称为 redo。数据通过重做日志的通道由一个不断增加的 LSN 值表示。
15.4.12.1 Group Commit for Redo Log Flushing
与任何其他兼容 ACID 的数据库引擎一样,InnoDB 在提交事务之前刷新事务的重做日志。InnoDB 使用 group commit 功能将多个此类刷新请求分组在一起,以避免每次提交一次刷新。使用 group commit 时,InnoDB 会向日志文件发出单一的写入操作,以执行在大约同一时间提交的多个用户事务的提交操作,从而显著提高吞吐量。
领取专属 10元无门槛券
私享最新 技术干货