InnoDB存储引擎体系结构如下图,主要分内存中和磁盘上的结构。
InnoDB结构图
主要包括缓冲池(Buffer Pool)、变更缓冲(Change Buffer)、日志缓冲(Log Buffer)以及自适应哈希索引(Adaptive Hash Index)技术。
Buffer Pool(BP)是 InnoDB 在启动时分配的一个内存区域,主要用于缓存访问过的表和索引等数据。利用缓冲池,可以合并一些经常访问数据的数据的操作,直接在内存处理,加快了处理的速度。
在专用的 MySQL 服务器上,通常会给缓冲池分配多达 80% 的物理内存。为了提高缓存管理的效率,使用页面链表的方式+LRU(最近最少使用)算法进行管理。
使用SHOW VARIABLES LIKE 'innodb_buffer_pool%';命令显示了 InnoDB 缓冲池相关的配置:
innodb_buffer_pool_chunk_size 表示每个缓冲块的大小;
innodb_buffer_pool_instances 表示缓冲池的实例个数,每个实例由数量相同的缓冲块组成;
innodb_buffer_pool_size 表示总的缓冲池大小,是 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 的倍数。
Change Buffer (CB):缓存了那些不在缓冲池中的二级索引(secondary index)页的修改操作。INSERT、UPDATE或者DELETE操作导致的变更将会在此缓冲,随后再合并(由其他读取操作引起)到缓冲池中。下图演示了变更缓冲的作用过程。
与聚集索引(clustered index)不同,二级索引通常是非唯一索引,索引的插入、更新、删除通常是顺序随机的操作。将变更进行缓存,并且在随后读入缓冲池时进行合并,能够避免将辅助索引页从磁盘读入缓冲池所需的大量随机 I/O。
当系统处于空闲状态或在缓慢关闭期间运行清除操作,定期将更新后的索引页写入磁盘。相对于每次将数据即写入磁盘,这种清除操作可以更有效地写入多个连续的索引值。
在内存中,变更缓冲属于缓冲池的一部分。在磁盘上,变更缓冲属于系统表空间的一部分;当数据库服务器关闭时,索引变更将会被缓冲到磁盘中。变更缓冲占用缓冲池空间,默认占25%,最大允许占50%,可以根据读写业务量来进行调整。参数innodb_change_buffer_max_size。
adaptive hash index(AHI):用于管理缓冲池中的内部数据结构,并对缓冲池的相关工作负载和内存操作组合进行自动调节,并且不会牺牲任何事务功能、性能和可靠性。
InnoDB 提供的磁盘存储组件主要包括系统表空间(System Tablespaces)、数据字典(Data Dictionary)、独立表空间(File-pre-Tablespaces)、临时表空间(Temporary Tablespace)、重做日志(Redo Log)、回滚日志(Undo Logs)以及双写缓冲(Doublewrite Buffer)。
System Tablespaces :包含InnoDB数据字典、双写缓冲、Chang Buffer磁盘部分和Undo logs,还包括在系统表空间创建的任何表和索引数据。它之所以被称为系统表空间,是因为它可以被多个用户表共享。系统表空间可以由一个或多个数据文件构成。在默认情况下,只会创建一个名为idbata1的共享表空间文件。但可以使用innodb_data_file_path启动选项控制共享表空间的数理和大小。
Data Dictionary:InnoDB数据字典由内部系统表组成,这些系统表包含用于跟踪对象(如表、索引、表列)的元数据。元数据存放在InnoDB系统表空间中。
双写缓冲是系统表空间中的一个存储区域;在 InnoDB 将缓冲池刷新到数据文件之前,会先将缓冲页写入该区域。如果在写入数据页的过程中,出现了操作系统、存储系统或者 mysqld 进程崩溃,InnoDB 可以利用双写缓冲存储的缓冲页进行故障恢复。
由于 InnoDB 的数据页大小往往和操作系统数据页大小不一致,例如 InnoDB 为 16 KB,操作系统为 4 KB;此时 InnoDB 刷新一个数据页,操作系统需要刷新 4 个数据页,在系统故障时可能只刷新了部分数据页。双写缓冲会先把缓冲池的数据写入共享表空间,然后再刷新数据页;如果在这个过程中发生系统崩溃,InnoDB 可以从共享表空间获取到要刷新的数据,然后重新执行写入。
虽然数据需要写入两次,双写缓冲并不会导致两倍的 I/O 负载或者操作,因为双写缓冲只需要写入一个连续的数据块,只有一次 fsync() 系统调用。
双写缓冲由系统变量 innodb_doublewrite 控制,默认值为 ON。如果文件系统或者存储设备提供了防止部分写失效的功能,可以禁用双写缓冲。
redo logs:在故障恢复期间使用基于磁盘的数据结构文件,用于恢复不完整提交事务写入的数据。在MySQL实例正常运行期间,重做日志对事务产生的数据变更部分进行编码并持久化到磁盘中(重做日志中的数据就是对受影响的行记录进行编码,利用这些编码数据把事务进行前滚的操作就叫作重做)在正常操作过程中,重做日志记录了表中的数据修改信息。当系统出现异常关闭后,重新启动时自动利用重做日志恢复未更新到数据文件中的修改。
默认情况下,重做日志物理上由两个文件 ib_logfile0 和 ib_logfile1 组成。MySQL 使用循环的方式写入重做日志文件。并使用一个不断增加的LSN值表示重做日志的写入量,以及标记写入重做日志文件中的位置。根据WAL(write-Ahead Logging,日志先行)原则,在提交事务时会先使用redo log持久化事务发生修改的部分数据(只要redo log落盘并打上commit 标记就表示事务已持久化)。
undo表空间,包含一个或多个undo log个文件,文件个数由配置参数innodb_undo_tablespaces控制。
回滚日志(undo log)由一组回滚日志记录组成,这些记录属于单个读写事务。回滚日志记录包含了回滚一个事务对聚集索引记录的最新修改所需的信息。另外,如果另一个事务需要查看原始的数据(一致性读),将会从回滚日志记录中返回未修改前的数据。
独立表空间用于存储单个 InnoDB 表的数据和索引,每个表空间在文件系统中对应单个数据文件。举例来说,如果我们为 test 数据库创建一个表 t1,MySQL 会在数据目录下的 test 子目录中创建一个数据文件 t1.idb。
InnoDB 默认使用独立表空间创建表,可以使用系统变量 innodb_file_per_table 进行控制。如果禁用该参数,InnoDB 将会默认在系统表空间中创建表。
通用表空间是一种共享的 InnoDB 表空间,可以供多个表和索引使用。在datadir路径下使用 CREATE TABLESPACE 语句创建的innoDB共享表空间。使用通用表空间比独立表空间具有更高的内存利用率。MySQL 服务器将会缓存表空间的元数据,包含多个表的通用表空间需要的内存比多个独立表空间更少。
临时表空间用于存放非压缩的InnoDB临时表和相关对象。
InnoDB 存在两种临时表空间:会话临时表空间(session temporary tablespaces)和一个全局临时表空间(global temporary tablespace)。
会话临时表空间用于存储用户创建的临时表;当 InnoDB 被设置为磁盘内部临时表的存储引擎时,会话临时表空间也用于优化器创建的内部临时表。从 MySQL 8.0.16 开始,磁盘内部临时表的存储引擎永远都是 InnoDB;在此之前由参数 internal_tmp_disk_storage_engine 决定 。
系统变量 innodb_temp_tablespaces_dir 决定了会话临时表空间的文件目录,默认为数据目录下的 #innodb_temp 子目录。 表 INFORMATION_SCHEMA.INNODB_SESSION_TEMP_TABLESPACES 存储了会话临时表空间的元数据,表 INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO 存储了当前活动的用户临时表的元数据。
全局临时表空间(ibtmp1)存储了用户临时表修改信息的回滚段数据。系统变量 innodb_temp_data_file_path 定义了全局临时表空间数据文件的相对路径、名称、大小以及属性。如果没有指定该参数,默认在 innodb_data_home_dir 目录中创建一个名为 ibtmp1 的自动扩展的数据文件,初始大小略微大于 12 MB。
如使用update语句:update test set idx=hello where id = 1,协同如下图:
(1)在Server层进行词法解析,解析成MySQL认识的语法,生成查询路径树,选择最优查询路径。
(2)到InnoDB存储引擎中,先判断id=1这行数据对应的页是否在缓冲池中,如果不在,则将id=1记录对应的页从datafile中读入InnoDB缓冲池,并对相关记录加独点锁。
(3)将idx修改为之前的值和对应的主键、事务ID原来的信息写入Undo Tablespace的回滚段中。
(4)更新缓存页中的数据,并将更新记录和新生成的LSN值写入Log buffer中,更新数据页数据,此时缓冲池这个页面为脏页了。
(5)在提交事务时,将log buffer中的更新记录刷新到redo log中,再写binlog,写完binlog后开始commit,binlog同步之后就是把binlog文件名和postition也写到redo log中。然后在redo log中写入一个commit标记,此时完成这个事务提交。接下来释放独占锁。
(6)后台I/O线程根据择机将缓存中合适的脏页刷新到磁盘数据文件中。当然刷新脏页时要先拷贝一份到双写缓冲区(当有开启双写缓冲区功能),当双写缓冲区中的数据落盘之后,再从缓冲池中把脏页刷新到各个数据文件中。
留言与评论(共有 0 条评论) “” |