目录
2.3 自适应哈希索引(Adaptive Hash Index)
3.4 InnoDB数据字典(InnoDB Data Dictionary)
1.总览
下图显示了组成InnoDB存储引擎体系结构的内存和磁盘体系结构。
2.InnoDB 内存体系结构
2.1 缓冲池(Buffer Pool)
缓冲池是一块InnoDB用来缓存表和索引数据的内存区域。缓冲池允许直接从内存中访问经常使用的数据,从而加快处理速度。在专用的服务器上,通常将80%的物理内存分配给缓冲池。
为了提高大容量读取操作的效率,缓冲池被分为多个页面,每个页面可能包含许多行记录。 为了提高缓存管理的效率,缓冲池被实现为页面的链接列表。 使用LRU算法的变体将很少使用的数据从缓存中淘汰掉。
知道如何利用缓冲池将经常访问的数据保留在内存中是MySQL优化的重要方面。
缓冲池LRU算法
缓冲池使用最少使用(LRU)算法的变体对页面链接列表进行管理。 当新页面需要添加到缓冲池时,将驱逐最近使用最少的页面,并将新页面添加到列表的中间。 中点插入策略将列表视为两个子列表:
- 插入点前面是最近访问过的年轻页面的子列表
- 插入点后面是最近访问过的旧页面的子列表
该算法将常用页面保留在新的子列表中。 旧的子列表包含不常用的页面;不常用页面是潜在的淘汰对象。
默认情况下,该算法的运行方式如下:
3/8的缓冲池空间用于旧的子列表。
列表的中点是新子列表的尾部与旧子列表的头相交的边界。当用户进行SQL查询或者InnoDB进行预读操作的时候,InnoDB将访问到的新页面插入缓冲池的中点(旧子列表的头部)。
访问旧子列表中的页面会使其“年轻”,并将其移至新子列表的开头。用户进行SQL查询读取页面则将立即进行首次访问并使页面年轻。如果因为InnoDB预读操作而读取了该页面,则第一次访问不会立即发生,并且在退出该页面之前可能根本不会发生。
随着数据库的运行,通过移到列表的末尾,缓冲池中未被访问的页面将“老化”。新的和旧的子列表中的页面都会随着其他页面的更新而老化。随着将页面插入中点,旧子列表中的页面也会老化。最终,未使用的页面到达旧子列表的尾部并被逐出。
2.2 更改缓冲区(Change Buffer)
更改缓冲区是一种特殊的数据结构。当辅助索引页面不在缓冲池时,更改缓冲区缓存INSERT,UPDATE和DELETE等DML操作对辅助索引页面的修改。缓存的修改将在以后通过其他读取操作将页面加载到缓冲池中时合并进去。
与聚簇索引不同,辅助索引通常是不唯一的,导致辅助索引中的插入更有可能以相对随机的顺序发生。同样,删除和更新可能会影响索引树中不相邻的辅助索引页。当稍后通过其他操作将受影响的页面读入缓冲池时,合并缓存的更改将避免从磁盘将辅助索引页读入缓冲池所需的大量随机访问I/O (内存操作的速度远大于磁盘操作的速度)。
在系统大部分处于空闲状态或缓慢关机期间运行的清除操作会定期将更新的索引页写入磁盘。与将每个值立即写入磁盘相比,清除操作可以更有效地为一系列索引值写入磁盘块(将需要保存的页面更改在磁盘上顺序批量保存,减少磁盘移臂开销)。
当有许多受影响的行和许多辅助索引要更新时,更改缓冲区合并可能需要几个小时。在此期间,磁盘I/O会增加,这可能会导致基于磁盘的查询速度大大降低。
在内存中,更改缓冲区是缓冲池的一部分。同时在磁盘上也有一个更改缓冲区。磁盘上的更改缓冲区是系统表空间的一部分,用于在数据库服务器关闭时保存内存更改缓冲区的内容。
如果索引包含降序索引列或主键包含降序索引列,则辅助索引不支持更改缓冲。
2.3 自适应哈希索引(Adaptive Hash Index)
自适应哈希索引功能使InnoDB在具有适当的工作负载和足够的缓冲池内存的系统上,可以更像内存数据库,而不会牺牲事务功能或可靠性。
通过对用户提交的查询进行观察,统计经常访问的索引页面并使用索引关键字的前缀构建哈希索引。 该前缀长度不限,也可以基于B树中的某些值建立哈希索引。
如果一个数据表能够全部加载到内存中,哈希索引将索引值转化为某种指针,启动直接查找任何元素的功能加速查询。InnoDB可以长期监视提交的辅助索引查询方式。 如果InnoDB注意到查询可以从构建哈希索引中受益,它会自动这样做。
在某些工作负载下,哈希索引查找的速度收益大大超过了监视索引查询和维护哈希索引结构的新增工作负载。在繁重的工作负载(海量并发的多连接)下,访问自适应哈希索引来提高查询速度是很有竞争力的。自适应哈希索引适合进行等值查询。 使用LIKE运算符和%通配符的查询也往往不会从自适应哈希索引中受益。 对于无法从自适应哈希索引功能中受益的工作负载,将其关闭可减少不必要的性能开销。 由于很难预先预测自适应哈希索引功能是否适合特定的系统和工作负载,因此请考虑启用和禁用该功能时运行基准测试。
2.4 日志缓冲区(Log Buffer)
日志缓冲区是日志存储区域,用于保存要写入磁盘上的日志文件数据。 日志缓冲区大小由innodb_log_buffer_size变量定义。 默认大小为16MB。 日志缓冲区的内容定期刷新到磁盘。 大型事务运行的时候,较大的日志缓冲区能够保证在事务提交之前不将重做日志数据写入磁盘。 因此,如果您有更新,插入或删除许多行的事务,则增加日志缓冲区的大小可以节省磁盘I / O。
3.InnoDB硬盘体系结构
3.1 表空间(Tablespaces)
3.1.1 系统表空间(The System Tablespace)
系统表空间是InnoDB数据字典,双写缓冲区,更改缓冲区和撤消日志的存储区。 如果在系统表空间中创建表,而不是在每个表文件或常规表空间中创建表,则它也可能包含表和索引数据。
系统表空间可以具有一个或多个数据文件。 缺省情况下,在数据目录中创建一个名为ibdata1的系统表空间数据文件。 系统表空间数据文件的大小和数量由innodb_data_file_path启动选项定义。 有关配置信息,请参阅系统表空间数据文件配置。
3.1.2 表文件表空间(File-Per-Table Tablespaces)
表文件表空间将单个InnoDB表的数据和索引存储到独立的数据文件。InnoDB默认情况下在每个表文件表空间中创建表。
mysql> USE test;
mysql> CREATE TABLE t1 (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100)
) ENGINE = InnoDB;
shell> cd /path/to/mysql/data/test
shell> ls
t1.ibd
表文件表空间的优点:
表文件表空间相对于系统表空间,通用表空间等共享表空间拥有以下优点:
- 在表文件表空间截断(truncate)和删除(drop)表后,释放的硬盘空间将归还给操作系统。截断或删除存储在共享表空间中的表会在共享表空间数据文件中创建可用空间,该可用空间仅可用于InnoDB数据。 换句话说,在表被截断或删除后,共享表空间数据文件的大小不会缩小。
- 对驻留在共享表空间中的表进行表复制ALTER TABLE操作会增加表空间占用的磁盘空间量。 此类操作可能需要与表中的数据以及索引一样多的额外空间。额外的空间不会像表文件表空间那样释放回操作系统。
- 在表文件表空间的表上执行TRUNCATE TABLE操作的性能相对于共享表空间性能更好。
- 可以在单独的存储设备上创建表文件表空间的不同数据文件,以进行I / O优化,空间管理或备份。
- 可以从另一个MySQL实例导入一个位于每表文件表空间中的表。
- 在表文件表空间中创建的表使用Barracuda文件格式。Barracuda文件格式使用了动态和压缩等多种行格式。
- 当发生数据损坏,备份或二进制日志不可用或无法重新启动MySQL服务器实例时,存储在单个表空间数据文件中的表可以节省恢复时间并提高成功的机会。
- 可以使用MySQL Enterprise Backup快速备份或还原在每表文件表空间中创建的表,而不会中断其他InnoDB表的使用。这对于具有不同备份计划的表或需要较少备份频率的表很有用。
- 表文件表空间允许通过监视表空间数据文件的大小来监视文件系统上的表大小。
- 当innodb_flush_method设置为O_DIRECT时,常见的Linux文件系统不允许并发写入共享表空间数据文件。当结合使用表文件表空间和该设置时,可能会提高并发性能。
- 共享表空间中的表的大小受到64TB表空间大小限制的限制。相比之下,表文件表空间的每个表都有64TB的大小限制,这为单个表的大小增加提供了足够的空间。
表文件表空间的缺点:
- 表文件表空间的表的未使用空间只能被同一张表使用。如果管理不当,则会浪费空间
- fsync操作是针对每个文件的,表文件表空间的每个表存放在单独的数据文件。因此无法合并多个表的写操作,这可能导致fsync操作的总数增加。
- mysqld必须为每个表文件表空间保留一个打开的文件句柄,表文件表空间中有许多表时可能会影响性能。
- 每个表都有其自己的数据文件时,需要更多的文件描述符。
- 表文件表空间的表使用单独的文件存储,无法实现文件间的剩余空间合并,可能存在更多的碎片妨碍DROP TABLE和表扫描性能。但是,表文件表空间可以提高管理碎片的性能。
- 删除驻留在每个表文件表空间中的表时,需要扫描缓冲池,对于大型缓冲池可能要花费几秒钟。扫描缓冲池会使用内部范围锁定执行扫描,这可能会延迟其他操作。
3.1.3 回滚表空间(Undo Tablespaces)
回滚表空间包含回滚日志,日志记录定义了怎样回滚事务对集群索引记录的最新更改,回滚日志是回滚日志记录的集合。 回滚日志位于回滚日志段,回滚日志段位于回滚段。innodb_rollback_segments变量定义分配给每个回滚表空间的回滚段数。
默认配置中,回滚日志位于系统表空间。但是回滚日志也可以通过更改配置存储在一个或多个回滚表空间中。 撤消日志的I/O模式使回滚表空间成为SSD存储的理想候选者,同时将系统表空间保留在硬盘存储上。
3.2 表(Tables)
InnoDB表及其索引可以在系统表空间,表文件表空间或常规表空间中创建。 启用innodb_file_per_table(这是默认设置)时,会在每个表的单个文件表空间中隐式创建一个InnoDB表。 相反,禁用innodb_file_per_table时,会在InnoDB系统表空间中隐式创建一个InnoDB表。 要在常规表空间中创建表,请使用CREATE TABLE ... TABLESPACE语法。
创建InnoDB表时,MySQL在MySQL数据目录下的数据库目录中创建一个.frm文件。对于在表文件表空间中创建的表,默认情况下,MySQL还在数据库目录中每个表创建一个.ibd表空间文件。在InnoDB系统表空间中创建的表是在现有ibdata文件中创建的,该文件位于MySQL数据目录中。在常规表空间中创建的表在现有的常规表空间.ibd文件中创建。常规表空间.ibd文件可以在MySQL数据目录内部或外部创建。
3.3 索引(Indexes)
3.3.1 聚集索引和二级索引
每个InnoDB表都有一个特殊的索引,称为聚簇索引,用于存储行数据。通常情况下,聚簇索引与主键是同一个意思。您必须了解InnoDB如何使用聚簇索引为每个表优化最常见的查找和DML操作。以便从查询,插入和其他数据库操作中获得最佳性能。
当在表上定义PRIMARY KEY时,InnoDB会将其用作聚簇索引。如果没有逻辑唯一且非空的列或列集,请添加一个新的自动填充递增列。
如果没有为表定义PRIMARY KEY,则MySQL会在所有索引列值都不为NULL的情况下找到第一个UNIQUE索引,InnoDB将其用作聚集索引。
如果表没有PRIMARY KEY或合适的UNIQUE索引,则InnoDB在包含行ID值的合成列上内部生成一个名为GEN_CLUST_INDEX的隐藏的聚集索引。数据行根据InnoDB分配给这个表中的合成列行ID值排序。行ID是一个6字节的字段,随着插入新行而单调增加。因此,行ID值随着数据行的插入递增。
聚集索引如何加快查询
通过聚集索引访问行是快速的,因为索引搜索直接访问到包含所有行数据的页面。与使用不同于索引记录的页面存储行数据的存储组织相比,聚集索引体系结构通常可以节省磁盘I / O操作。
二级索引如何与聚簇索引关联
除聚集索引以外的所有索引都称为辅助索引。在InnoDB中,二级索引中的每个记录都包含该行的主键列以及为二级索引指定的列。 InnoDB使用此主键值在聚集索引中搜索行。
3.3.2 索引的物理结构
除了空间索引,InnoDB索引都是B树数据结构。空间索引使用R树,R树是用于索引多维数据的专用数据结构。索引记录存储在其B树或R树数据结构的叶页中。索引页的默认大小为16KB。
当新记录插入到InnoDB聚集索引中时,InnoDB尝试使页面的1/16空闲,以备将来插入和更新索引记录。如果按顺序插入索引记录(升序或降序),则所得到的索引页大约为15/16。如果以随机顺序插入记录,则页面的容量为1/2到15/16。
InnoDB在创建或重建B树索引时执行批量加载。这种索引创建方法称为排序索引构建。 innodb_fill_factor配置选项定义了在排序索引构建期间填充的每个B树页面上的空间百分比,剩余的空间保留用于将来的索引增长。空间索引不支持排序索引构建。设置为100的innodb_fill_factor可使群集索引页中的空间的1/16留给将来的索引增长。
如果InnoDB索引页的填充因子下降到MERGE_THRESHOLD(如果未指定,默认值为50%)以下,则InnoDB尝试收缩索引树以释放页面。 MERGE_THRESHOLD设置适用于B树索引和R树索引。
您可以通过在初始化MySQL实例之前设置innodb_page_size配置选项来定义MySQL实例中所有InnoDB表空间的页面大小。定义实例的页面大小后,如果不重新初始化实例就无法更改它。支持的大小为64KB,32KB,16KB(默认),8KB和4KB。
3.3.3 排序索引构建
在创建或重建索引时,InnoDB执行批量加载,而不是一次插入一个索引记录。这种索引创建方法也称为排序索引构建。空间索引不支持排序索引构建。
索引构建分为三个阶段。在第一阶段,将扫描聚簇索引,并生成索引条目并将其添加到排序缓冲区。当排序缓冲区已满时,将对条目进行排序并将其写到临时中间文件中。此过程也称为“运行”。在第二阶段中,将一个或多个运行写入临时中间文件,对文件中的所有条目执行合并排序。在第三个也是最后一个阶段,将已排序的条目插入到B树中。
在引入排序索引构建之前,InnoDB使用插入API将索引条目一条条的插入到B树中。具体操作步骤:1. 打开B树光标以找到插入位置,2. 使用乐观插入将条目插入B树页面。如果由于页面已满而导致插入失败,则将执行悲观插入。整个过程涉及打开B树游标和并根据需要拆分和合并B树节点以找到正确位置的条目空间。这种“自顶向下”建立索引方法的缺点是搜索插入位置的开销以及B树节点的不断分裂和合并的高成本。
排序索引构建使用“自下而上”的方法来构建索引。通过这种方法,B树所有高度的节点都保留了对最右边的叶子页的引用。在特定的B树深度节点处分配最右边的叶子页,并根据其排序顺序插入条目。叶子页已满后,节点指针回退到父页,并且为下一个插入分配同级叶子页。此过程将一直持续到插入所有条目为止,这可能会导致插入一直上升到根节点。分配同级页面后,将释放对先前固定的叶子页面的引用,并且新分配的叶子页面将成为最右边的叶子页面和新的默认插入位置。
排序索引构建和重做日志记录
在排序索引构建期间,重做日志记录被禁用。而是有一个检查点来确保索引构建可以承受意外的退出或失败。该检查点强制将所有脏页写入磁盘。在排序索引的构建过程中,将定期向页面清洁器线程发送信号以刷新脏页面,以确保可以快速处理检查点操作。通常当清除页数低于设置的阈值时,页面清除程序线程将刷新脏页。对于排序的索引生成,脏页将立即刷新,以减少检查点开销并并行化I / O和CPU活动。
排序索引构建和优化器统计
排序的索引构建可能会导致优化器统计信息与以前的索引创建方法所生成的统计信息不同。统计信息的差异(预计不会影响工作负载性能)是由于用于填充索引的算法不同。
3.4 InnoDB数据字典(InnoDB Data Dictionary)
InnoDB数据字典由内部系统表组成,数据字典的内部系统表用于跟踪对象(例如表,索引和表列)的元数据。 InnoDB数据字典位于InnoDB系统表空间中。 由于历史原因,数据字典元数据在某种程度上与InnoDB表元数据文件(.frm文件)中存储的信息重叠。
3.5 双写缓冲(Doublewrite Buffer)
双写缓冲区是一个存储区域,InnoDB会先在双写缓冲区写入从缓冲池中刷新出来的页面,然后再将页面写入InnoDB数据文件中的适当位置。 如果在页面写入过程中存在操作系统,存储子系统或意外的mysqld进程退出,则InnoDB可以在崩溃恢复期间从双写缓冲区中试着找到页面的正确副本。
尽管数据被写入两次,但双写缓冲区并不需要两倍的I/O开销或两次的I/O操作。 只需对操作系统进行一次fsync()调用,就可以将较大的数据块顺序写入双写缓冲区(除非innodb_flush_method设置为O_DIRECT_NO_FSYNC)。
在大多数情况下,默认情况下会启用双写缓冲区。 要禁用双写缓冲区,请将innodb_doublewrite设置为0。
3.6 重做日志(Redo Log)
重做日志是基于磁盘的数据结构,在崩溃恢复期间用于恢复被中断的事务需要写入的数据。在正常操作期间,重做日志对更改请求数据的请求(由SQL语句或低级API调用引起的)进行编码。服务重启后在初始化期间以及接受连接之前,会自动重放服务器意外关闭之前未完成更新数据文件的修改,保证事务的一致性和持久性。
缺省情况下,重做日志在磁盘上由名为ib_logfile0和ib_logfile1的两个文件物理表示。 MySQL以循环方式写入重做日志文件。重做日志中的数据按照受影响的记录进行编码;此数据统称为重做。通过重做日志的数据传递以不断增加的LSN值表示。
3.7 回滚日志(Undo Logs)
回滚日志是与单个读写事务关联的回滚日志记录的集合。回滚日志记录包如何撤消事务对聚簇索引记录的最新更改。如果另一个事务需要将原始数据视为一致性读的一部分,则将从回滚日志记录中检索未修改前的数据。回滚日志存在于回滚日志段,回滚日志段存在于回滚段。回滚段位于系统表空间,撤消表空间和临时表空间中。
InnoDB最多支持128个回滚段,其中32个分配给临时表空间。剩下的96个回滚段可以分配给修改常规表中数据的事务。 innodb_rollback_segments变量定义InnoDB使用的回滚段数。
回滚段支持的事务数取决于回滚段中的回滚插槽数和每个事务所需的回滚日志数。
一个事务最多一次可以分配四个回滚日志,以下每种操作类型都可以分配一个:
- 用户定义的表执行INSERT操作
- 用户定义表上的UPDATE和DELETE操作
- 在用户定义的临时表上执行INSERT操作
- 用户定义的临时表上的UPDATE和DELETE操作
回滚日志根据需要分配。例如,对常规表和临时表执行INSERT,UPDATE和DELETE操作的事务需要分配四个回滚日志。仅对常规表执行INSERT操作的事务需要一个回滚日志。
系统表空间或回滚表空间回滚段中分配了对常规表执行操作的事务的回滚日志。从指定的临时表空间回滚段中分配了对临时表执行操作的事务的回滚日志。
分配给事务的回滚日志在事务持续的时间段内与事务一直绑定。例如,为常规表上的INSERT操作分配给事务的回滚日志将用于该事务对常规表上的所有INSERT操作。