暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

innodb 的体系结构


上图 innodb 存储引擎的架构引用官方手册,从上图来看关于架构的相关功能看起来很复杂,实际上也确实很复杂。为了方便理解我用黄红绿三个框稍微给归纳一下,分成三个部分。

简单的概括一下,当然没有很全面。

黄框是关于内存方面的功能;

红框是将数据从内存落实到硬盘的相关功能;

绿框是硬盘表空间的相关功能;

缓冲池(bufferpool):

缓冲池主要是通过内存提升数据库运行效率,解决硬盘运行速度较慢的缺点。在内存中分配一块区域给缓冲池,然后将数据库查询到的数据放在划分的内存中。如果下次再读取相同的数据,首先会去内存中找,找到了直接从内存中取走。找不到再去硬盘中读取。

以上是读取的操作,对于修改的操作则会先修改内存上的数据再通过 checkpoint 刷新回硬盘,这样做主要是减少磁盘的 io,提高性能。

而缓冲区的大小将会直接影响 Mysql 数据库的性能,通过”SHOW VARIABLES LIKE 'innodb_buffer_pool_size’\G; 命令可以查看缓冲池大小。

缓冲池的管理:

缓冲池通过 LRU 算法来管理放入内存中的数据,因为内存就算设置的再大也不可能存放所有的数据,既然只能存放一部分那肯定就是要存放最有 “价值“的那一部分,而这个价值指的就是” 访问频率 “。lru 会根据访问频率将数据按页的规格从头到尾的存放,访问频率较低的放在尾部,高的放在头部。如果加入缓冲池的数据超过了设置的大小就会从尾部开始将数据释放出缓冲池,并且进行了一定的优化,引入了”midpoint“算法。为什么要引入这个算法,可以想象一下,假如有一个缓冲池 1G 大小。里面已经存放了 1G 的数据,前 300M 是访问频繁的数据,后面 700M 是频率较低的数据。此时数据库产生了新的 300M 数据放在缓冲池中,刚才尾部的 700M 被” 挤“出去 300M 剩 400M。而刚才头部的 300M 被”挤“到中间的位置。这是产生的数据没有超过缓冲池大小的情况如果产生的数据超过了 1G 呢。比如查询一个比较少用到的表,这个操作可以一个月做不了几次,但是会产生大量的数据比如正好 1G。上文说过每个操作产生的数据都会放入内存中,那这个 1G 不常用的数据是不是正好将 300M 常用的数据顶出去了。所以引用了”midpoint“算法,产生的新的数据不会存放在头部位置,而是大概 3/8 的位置,这样就保证了前 3/8 是真正的热点数据不会被释放出去。midpoint 之前的页是 new list,之后的页是 old list。如果是 old list 的数据被访问到了,这个页信息就会变成 new list,变成 young page,就会将数据页信息移动到头部。加入了一个参数” innodb_old_blocks_time“表示新加入在 midpoint 点的数据必须保持多少秒,才能加入到 new 页。



“change buffer”在 innodb 的 1.0 版本之前叫 “insert buffer” 主要用于辅助索引 (二级索引,非聚簇索引) 的 dml 操作。

什么是 “聚簇索引” 和“辅助索引”,聚簇索引就是选择主键建立的索引如果没有选择主键就会选择唯一索引,没有唯一索引就会自动建立一个隐藏的 rowid 作为聚簇索引。聚簇索引存放的数据一般都是顺序且唯一,并且在叶子节点会存放该索引一整行的数据,读取聚簇索引的数据一般不会产生磁盘的随机读写速度较快。而辅助索引的列一般非唯一且重复操作辅助索引的数据会造成大量的磁盘随机读写影响效率。

综上所述为此引入了 “change buffer” 的功能来改善这一情况,之所以改名是因为在 1.0 版本后 Insert、Delete、Update。都进行了缓冲。

而 change buffer 的作用就是,在执行插入操作时先去缓冲池查找看辅助索引的也在不在缓冲池,如果在就直接插入。不在就放到 change buffer 中,然后按照设定的时间规律集中的 merge 操作。因为此时常常会是多个插入操作同时 merge,所以会提高辅助索引的插入性能。merge 的操作通常在三种情况下会执行;

一:系统比较空闲时merge ;

二:缓冲池的空间不够时;

三:数据库正常关闭时;

四:redo log 写满时;

相关的参数:

innodb_change_buffer_max_size 允许将更改缓冲区的最大大小配置为缓冲池总大小的百分比。默认 25% 最大 50%。

log buffer:日志缓冲区就是存储要写入到 redo log(磁盘上的文件) 的内存区域。它的内容会定期的刷新到磁盘。如果业务上有大量 dml 相关的事务,增加此缓存大小可以减少磁盘 io。

相关参数如下:

 innodb_flush_log_at_trx_commit 控制日志缓冲区的内容如何写入和刷新到磁盘。该 innodb_flush_log_at_timeout 变量控制日志刷新频率。


 System Tablespace(系统表空间)

如图所示,系统表空间是change buffer的存储区域。如果你的表是在这个表空间创建不是独立表空间或者通用表空间,则该表空间还会有表和索引数据。系统表空间数据文件的大小和数量由innodb_data_file_path定义。


8.0之前:

1.系统表空间包含InnoDB数据字典;

2.系统表空间还包含双写缓冲区存储区域;

8.0之后:

1.InnoDB将元数据存储在 MySQL 数据字典中;

2.MySQL 8.0.20 起,双写缓冲区存储区域位于单独的双写文件中;


File-Per-Table 表空间(独立表空间)

如图所示,独立表空间的表会有一个单独以表命名的.ibd文件,该功能由 innodb_file_per_table控制,默认开启。

优点:

1.删除独立表空间的表,会缩小实际磁盘使用量。共享表空间的表不会。

2.在共享表空间中的表上执行复制表的ALTER TABLE操作,可能会增加表空间占用的磁盘空间。

3.TRUNCATE TABLE在 file-per-table 表空间中的表上执行时性能更好。

4.File-per-table 表空间数据文件可以在单独的存储设备上创建,方便对表基于业务需求的优化和管理。

5.在 file-per-table 表空间中创建的表支持与DYNAMIC行 COMPRESSED格式相关联的功能,而系统表空间不支持这些功能。

6.存储在单个表空间数据文件中的表可以节省时间并提高在发生数据损坏、备份或二进制日志不可用或无法重新启动 MySQL 服务器实例时成功恢复的机会。

7.在 file-per-table 表空间中创建的表可以使用 MySQL Enterprise Backup 快速备份或恢复,而不会中断其他 InnoDB表的使用。

8.File-per-table 表空间可以通过查看表空间数据文件的大小来判断文件系统上的表大小。

缺点:

1.使用 file-per-table 表空间,每个表可能有未使用的空间,只能由同一个表的行使用,可能会导致空间浪费。

2.fsync操作是在多个 file-per-table 数据文件而不是单个共享表空间数据文件上执行的。因为 fsync操作是针对每个文件的,所以不能合并多个表的写操作,这会导致fsync 操作总数增加。3.mysqld必须为每个 file-per-table 表空间保留一个打开的文件句柄,如果在 file-per-table 表空间中有许多表,这可能会影响性能。

4.当每个表都有自己的数据文件时,需要更多的文件描述符。

5.可能会出现更多碎片,这会影响 DROP TABLE表扫描性能。但是,如果碎片得到管理,每个表的文件表空间可以提高这些操作的性能。

6.删除驻留在 file-per-table 表空间中的表时会扫描缓冲池,这对于大型缓冲池可能需要几秒钟。扫描是使用广泛的内部锁执行的,这可能会延迟其他操作。

7.该 innodb_autoextend_increment 变量定义了在自动扩展共享表空间文件变满时扩展其大小的增量大小,不适用于 file-per-table 表空间文件,无论 innodb_autoextend_increment 设置如何,它们都会自动扩展。最初的 file-per-table 表空间扩展是少量的,之后扩展以 4MB 的增量出现。


通用表空间

  • 与系统表空间类似,通用表空间是能够为多个表存储数据的共享表空间。

临时表空间

会话临时表空间存储用户创建的临时表和优化器在InnoDB配置为磁盘内部临时表的存储引擎时创建的内部临时表。

全局临时表空间 ( ibtmp1) 存储回滚段,用于对用户创建的临时表所做的更改。


双写缓冲区

双写缓冲区是存在内存和磁盘上的一个存储区域,用于存放即将从缓冲池写入到磁盘上的数据,如果在这个写入过程中遇到意外情况导致数据丢失。仅仅依靠redo日志是无法还原丢失的这一部分数据。但是可以从双写缓冲区中还原。


redo log

重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。 该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中, 用于在刷新脏页到磁盘,发生错误时, 进行数据恢复使用。

在InnoDB引擎中的内存结构中,主要的内存区域就是缓冲池,在缓冲池中缓存了很多的数 据页。 当我们在一个事务中,执行多个增删改的操作时,InnoDB引擎会先操作缓冲池中的数据,如果缓冲区没有对应的数据,会通过后台线程将磁盘中的数据加载出来,存放在缓冲区中,然后将缓冲池中的数据修改,修改后的数据页我们称为脏页。 而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。 而缓冲区的脏页数据并不是实时刷新的,而是一段时间之后将缓冲区的数据刷新到磁盘中,假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却没有持久化下来,这就出现问题了,没有保证事务的持久性。



有了redolog之后,当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在redo log buffer中。在事务提交时,会将redo log buffer中的数据刷新到redo log磁盘文件中。 过一段时间之后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于redo log进行数据恢复,这样就保证了事务的持久性。 而如果脏页成功刷新到磁盘 或 或者涉及到的数据已经落盘,此时redolog就没有作用了,就可以删除了,所以存在的两个redolog文件是循环写的。

那为什么每一次提交事务,要刷新redo log 到磁盘中呢,而不是直接将buffer pool中的脏页刷新到磁盘呢 ?

因为在业务操作中,我们操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。 而redo log在往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。 这种先写日志的方式,称之为 WAL(Write-Ahead Logging)。

undo log

回滚日志,用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚(保证事务的原子性) 和 MVCC(多版本并发控制) 。 undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的 update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。 

Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些 日志可能还用于MVCC。 

Undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含1024个undo log segment。

最后修改时间:2023-10-25 15:02:04
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论