不了解InnoDB体系架构怎么能用好InnoDB?

634 阅读10分钟

前言

上篇文章讲解了 MySQL 体系架构,今天讲解 MySQL 最主流的存储引擎 InnoDB 体系结构。

InnoDB 存储结构

InnoDB 逻辑存储单元主要分为:表空间、段、区、页。层级关系如下图:

表空间

MySQL5.7以后,表空间分为:系统表空间、独立表空间、通用表空间、undo表空间、临时表空间。

系统表空间

系统表空间以 ibdata1 命名,新建一个数据库时,InnoDB 存储引擎会初始化一个名为 ibdata1 的表空间文件,默认情况下,这个文件存储所有表的数据,比如看不到的系统表,此外,还会存储数据字典、双写缓冲区、更新缓冲区和undo log。MySQL 5.6 以后,已经可以通过参数来设置 undo log 的存储位置了,可以独立于 ibdata1 文件进行存储。

如果系统表空间大小不够,可以配置为自动扩展,使用以下参数指定系统表空间的路径、初始化大小、自动扩展策略。数据库默认的自动扩展大小是 64MB,当然还可以通过添加另一个数据文件来增加系统表空间的大小。

innodb_data_file_path

在这里插入图片描述

MySQL 默认 ibdata1 大小是 12MB(Mysql 8),一般不建议使用默认大小,在遇到高并发事务时,会受到很大影响,建议把 ibdata1 的初始数值大小调整为 1GB

独立表空间

除了系统表空间还有独立表空间(File-per-table),每个表的表空间包含单个 InnoDB 表的数据和索引,并存储在文件系统上的单个数据文件中。

MySQL 每创建一个表,就会生成一个独立表空间,是以 table_name.db 命名的。

系统表空间和独立表空间有什么区别?

  • 截断或删除独立表空间中创建的表后,磁盘空间将被释放,而截断或删除系统表空间中的表会在系统表空间数据文件中创建空闲空间,此空间只能用于存储 InnoDB 数据,也就是系统表空间在表截断或删除后占用系统空间不会缩小。
  • 可以从另一个 MySQL 实例导入独立表空间中的表,而系统表空间不行
  • 更多请查看官网…

通用表空间

与系统表空间类似,通用表空间是能够为多个表存储数据的共享表空间。可以根据活跃度来划分表,存放在不同的磁盘上,可以减少 metadata 的存储开销。

undo表空间

undo表空间包含undo log,这是包含有关如何撤消事务对聚集索引记录的最新更改的信息的记录集合。

undo日志默认存储在系统表空间中,但也可以存储在一个或多个undo表空间中。使用undo表空间可以减少任何一个表空间中undo log所需的空间量。undo log 的 I/O 模式也使 undo 空间成为SSD存储的理想选择 。

临时表空间

把临时表的数据从系统表空间中抽离出来,形成独立的表空间参数 innodb_temp_data_file_path ,独立表空间文件名为 ibtmp1,默认大小 12MB
在这里插入图片描述

表空间是由段组成的,也可以把一个表理解为一个段,通常由数据段、回滚段、索引段等,每个段由 N 个区和 32 个零散的页组成,段空间扩展是以区为单位进行扩展的,通常情况下,创建一个索引的同时会创建两个段,分别为非叶子节点和叶子节点段。

区是由连续的页组成的,是物理上连续分配的一段空间,每个区的大小固定是 1MB

InnoDB 的最小物理存储单位是页,有数据页回滚页等。一般情况下,一个区由 64 个连续的页组成,页默认大小是 16KB,可以自行调整页的大小。区也就是 64*16KB = 1MB

页里又记录着行记录的信息,InnoDB存储引擎是面向列的,也就是数据是按行存储的。行记录格式可以分为四种:Compact,dynamic,redundant,compressed,MySQL5.7 默认使用 dynamic 行记录格式。Compact 是目前使用最多的一种,但默认是 dynamic ,二者有什么不同?

行溢出简单来讲就是需要存储的数据在当前存储页面外,拆分到多个页进行存储。针对大数据类型 text 或者 blob 存储在其字段中的数据,dynamic 实际采用的数据都存放在溢出的页中,而数据页只存前20个字节的指针。在 compact 行格式下,溢出的列只存放 768 个前缀字节,dynamic 这种行格式模式,针对溢出列所在的新页利用率会更高,所以目前生产环境中建议使用 dynamic 行格式存储。

redundant 是最早的行记录格式,相比 compact 消耗更多存储空间,不建议使用。

compressed 是压缩行格式,对数据和索引页进行压缩。但只是针对物理层面的压缩,在内存中是不压缩的,当数据调用到内存中就涉及转换,会有很多无用的CPU消耗,而且效率也低。压缩比例也不高,大概1/2 的比例,压缩会使数据库的TPS下降,很影响线上业务,不建议使用。

InnoDB 体系架构

InnoDB 体系结构主要由三部分组成:

  • 内存结构
  • 线程
  • 磁盘文件

内存结构

内存结构中有包括缓冲池(innodb_buffer_pool)、重做日志缓冲(redo log_buffer)、额外内存池(innodb_additional_mem_pool_size)


innodb_buffer_pool

在数据库系统中,CPU与磁盘速度之间存在极大速度差异,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库整体性能。缓冲池是一块内存区域,通过内存速度弥补磁盘速度较慢带来的影响。

数据库读取页操作,首先将从磁盘读到的页存放到缓冲池中,称为将页fix到缓冲池中。下次读取相同页时,首先判断缓冲池中是否有该夜,如果有,则命中,直接读取该页,如果没有,在进行fix的过程。

数据库中页的修改操作,先修改缓冲池中的页,然后再以一定的频率刷到磁盘上。从缓冲池刷回磁盘操作并不是在每次页发生更改后进行,而是通过一种checkPoint机制刷回磁盘。

缓冲池的大小直接影响着数据库的整体性能。InnoDB存储引擎而言,缓冲池的配置通过参数innodb_buffer_pool_size 来设置。

缓冲池中的数据类型有:索引页(index page),数据页(data page),插入缓冲(change buffer),自适应哈希索引(Adaptive hash index),锁信息(lock info),数据字典信息(dictionary cache)

缓冲池满置换算法

LRU List

数据库中的缓冲池是通过 LRU 算法进行管理的,最近使用最频繁的页放在LRU列表的最前端,使用最少的页放在LRU列表的末端,当缓冲池不能存放新数据时,首先释放 LRU 列表末端的页。

FREE List

LRU管理已经读取的页,数据库启动时,LRU列表是空的,没有页。此时的页都放在 Free List ,当需要从缓冲池中分页时,先查看 Free List 是否有空闲的页,若有,则从 Free List 中删除该页,放入到 LRU 列表。

FLUSH List

在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页数据不一致。这时会通过checkpoint机制将脏页刷新回磁盘,而Flush list中的页即为脏页列表。


InnoDB 三大特性:

插入缓冲、两次写、自适应哈希索引构成了 InnoDB 三大特性,这些特性让 InnoDB 存储引擎有了更好的性能和可靠性。

插入缓冲(insert buffer)

影响数据库最主要的性能问题就是 I/O,而插入缓冲的作用就是把普通索引上的 DML 操作从随机 I/O 变成顺序 I/O,提高 I/O 效率。 它的工作原理很简单,就是先判断插入的普通索引页是否在缓冲池中,如果在就可以直接插入,如不在就要先放到 change buffer 中,然后进行 change buffer 和普通索引的合并操作,可以将多个插入合并到一个操作中,一下子就提高了普通索引的性能。

两次写(double write)

插入缓冲带来的是针对普通索引插入性能上的提升,而 double write 是保证写入的安全性,防止 MySQL 宕机时,InnoDB 发生数据页部分页写的问题。如果数据库实例崩溃,是不是可以通过 redo log 进行恢复,但 redo log 文件记录的是页的物理操作,如果页都损坏了,是无法进行任何恢复操作的。所以需要一个页的副本,如果实例宕机了,可以先通过副本把原来的页还原出来,再通过 redo log 进行恢复、重做。这就是 double write 的作用。

自适应哈希索引(Adaptive hash index)

InnoDB存储引擎有一个机制,可以监控索引的搜索,如果 InnoDB 注意到查询可以通过建立哈希索引得到优化,那么就会自动完成这件事。可以通过 innodb_adaptive_hash_index 参数来控制。innodb_adaptive_hash_index 默认是开启的。

从 MySQL5.7开始,自适应哈希索引搜索系统是分区的,每个索引都会绑定到一个特殊的分区上,而且各个分区都有自己的锁存器来进行保护。分区可以通过 innodb_adaptive_hash_index_parts 参数来控制,该参数默认值为8,最大可以设置为 512,通过设置分区值,可以降低争用,提高并发性。

在这里插入图片描述

Redo Log Buffer

是一块内存区域,用来缓存即将被写入到 redo log 的数据,InnoDB 引擎首先将 redo log 信息缓存到 redo log 缓冲中,然后定期将其刷新到磁盘上的 redo log 文件。

以下三种情况会将 redo log 缓冲区中的内容刷新到外部磁盘 redo log 文件中。

  • Master Thread 每一秒将 redo log 缓冲刷新到 redo log 文件中
  • 每个事物提交时会将 redo log 缓冲刷新到 redo log 文件中
  • 当 redo log 缓冲池剩余空间小于 1/2 时,redo log 缓冲刷新到 redo log 文件中

Innodb_additional_mem_pool_size

Innodb_additional_mem_pool_size 是 InnoDB 用来保存数据字典信息和其他内部数据结构的内存池的大小,单位是 byte,参数默认值为 8M,数据库中表数量越多,参数值应该越大,如果 InnoDB 用完了内存池中的内存,就会从操作系统中分配内存,同时在 error log 中打入报警信息。

线程

后台线程包括了 Master Thread,Page Cleaner Thread,Read thread/Write Thread,Redo log Thread 和 Change buffer Thread

  • Master Thread 主要负责将缓冲池中的数据异步刷新到磁盘,保证数据一致性,包括脏页的刷新、合并插入缓冲。
  • Page Cleaner Thread 将原本放在 Master Thread 中进行的脏页刷新操作翻到了单独的线程中来完成,是 InnoDB1.2.x版本后引入的。
  • Read thread/Write Thread 是数据库的读写请求线程,默认值都是4个,如果使用高转速磁盘,可适当调大该值。
  • Redo log Thread 负责把日志缓冲中的内容刷新到 redo log 文件中。
  • Change buffer Thread 负责把插入缓冲中的内容刷新到磁盘

磁盘文件

InnoDB 引擎的表都会生成一个 .ibd 文件,其中存储着数据信息和索引信息。

总结

本文主要讲述了 MySQL 的主流存储引擎 InnoDB 的体系架构,如对 MySQL 感兴趣可继续关注 MySQL 专栏。