大家好,我是易安!
明天咱们谈一谈 MySQL 中 InnoDB 存储引擎。InnoDB 存储引擎作为咱们最罕用到的存储引擎之一,充沛相熟它的的实现和运行原理,有助于咱们更好地创立和保护数据库表。
InnoDB 体系架构
InnoDB 次要包含了内存池、后盾线程以及存储文件。内存池又是由多个内存块组成的,次要包含缓存磁盘数据、redo log 缓冲等;后盾线程则包含了 Master Thread、IO Thread 以及 Purge Thread 等;由 InnoDB 存储引擎实现的表的存储构造文件个别包含表构造文件(.frm)、共享表空间文件(ibdata1)、独占表空间文件(ibd)以及日志文件(redo 文件等)等。
1. 内存池
咱们晓得,如果客户端从数据库中读取数据是间接从磁盘读取的话,无疑会带来肯定的性能瓶颈,缓冲池的作用就是进步整个数据库的读写性能。
客户端读取数据时,如果数据存在于缓冲池中,客户端就会间接读取缓冲池中的数据,否则再去磁盘中读取;对于数据库中的批改数据,首先是批改在缓冲池中的数据,而后再通过 Master Thread 线程刷新到磁盘上。
实践上来说,缓冲池的内存越大越好。缓冲池中不仅缓存索引页和数据页,还包含了 undo 页,插入缓存、自适应哈希索引以及 InnoDB 的锁信息等等。
InnoDB 容许多个缓冲池实例,从而缩小数据库外部资源的竞争,加强数据库的并发解决能力。
InnoDB 存储引擎会先将重做日志信息放入到缓冲区中,而后再刷新到重做日志文件中。
2. 后盾线程
Master Thread 次要负责将缓冲池中的数据异步刷新到磁盘中,除此之外还包含插入缓存、undo 页的回收等,IO Thread 是负责读写 IO 的线程,而 Purge Thread 次要用于回收事务曾经提交了的 undo log,Pager Cleaner Thread 是新引入的一个用于帮助 Master Thread 刷新脏页到磁盘的线程,它能够加重 Master Thread 的工作压力,缩小阻塞。
3. 存储文件
在 MySQL 中建设一张表都会生成一个.frm 文件,该文件是用来保留每个表的元数据信息的,次要蕴含表构造定义。
在 InnoDB 中,存储数据都是按表空间进行寄存的,默认为共享表空间,存储的文件即为共享表空间文件(ibdata1)。若设置了参数 innodb\_file\_per\_table 为 1,则会将存储的数据、索引等信息独自存储在一个独占表空间,因而也会产生一个独占表空间文件(ibd)。如果你对共享表空间和独占表空间的了解还不够透彻,接下来我会详解。
而日志文件则次要是重做日志文件,次要记录事务产生的重做日志,保障事务的一致性。
InnoDB 逻辑存储构造
InnoDB 逻辑存储构造分为表空间(Tablespace)、段 (Segment)、区(Extent)、页 Page) 以及行(row)。
1. 表空间(Tablespace)
InnoDB 提供了两种表空间存储数据的形式,一种是共享表空间,一种是独占表空间。InnoDB 默认会将其所有的表数据存储在一个共享表空间中,即 ibdata1。
咱们能够通过设置 innodb\_file\_per\_table 参数为 1(1 代表独占形式)开启独占表空间模式。开启之后,每个表都有本人独立的表空间物理文件,所有的数据以及索引都会存储在该文件中,这样不便备份以及复原数据。
2. 段(Segment)
表空间是由各个段组成的,段个别分为数据段、索引段和回滚段等。咱们晓得,InnoDB 默认是基于 B + 树实现的数据存储。
这里的索引段则是指的 B + 树的非叶子节点,而数据段则是 B + 树的叶子节点。而回滚段则指的是回滚数据,MVCC 就是利用了回滚段实现了多版本查问数据。
3. 区(Extent) / 页(Page)
区是表空间的单元构造,每个区的大小为 1MB。而页是组成区的最小单元,页也是 InnoDB 存储引擎磁盘治理的最小单元,每个页的大小默认为 16KB。为了保障页的连续性,InnoDB 存储引擎每次从磁盘申请 4 - 5 个区。
4. 行(Row)
InnoDB 存储引擎是面向行的(row-oriented),也就是说数据是按行进行寄存的,每个页寄存的行记录也是有硬性定义的,最多容许寄存 16KB/2-200 行,即 7992 行记录。
InnoDB 事务之 redo log 工作原理
InnoDB 是一个事务性的存储引擎,而 InnoDB 的事务实现是基于事务日志 redo log 和 undo log 实现的。redo log 是重做日志,提供再写入操作,实现事务的持久性;undo log 是回滚日志,提供回滚操作,保障事务的一致性。
redo log 又包含了内存中的日志缓冲(redo log buffer)以及保留在磁盘的重做日志文件(redo log file),前者存储在内存中,容易失落,后者长久化在磁盘中,不会失落。
InnoDB 的更新操作采纳的是 Write Ahead Log 策略,即先写日志,再写入磁盘。当一条记录更新时,InnoDB 会先把记录写入到 redo log buffer 中,并更新内存数据。咱们能够通过参数 innodb\_flush\_log\_at\_trx\_commit 自定义 commit 时,如何将 redo log buffer 中的日志刷新到 redo log file 中。
在这里,咱们须要留神的是 InnoDB 的 redo log 的大小是固定的,别离有多个日志文件采纳循环形式组成一个循环闭环,当写到结尾时,会回到结尾循环写日志。咱们能够通过参数 innodb\_log\_files\_in\_group 和 innodb\_log\_file\_size 配置日志文件数量和每个日志文件的大小。
Buffer Pool 中更新的数据未刷新到磁盘中,该内存页咱们称之为脏页。最终脏页的数据会刷新到磁盘中,将磁盘中的数据笼罩,这个过程与 redo log 不肯定有关系。
只有当 redo log 日志满了的状况下,才会被动触发脏页刷新到磁盘,而脏页不仅只有 redo log 日志满了的状况才会刷新到磁盘,以下几种状况同样会触发脏页的刷新:
- 零碎内存不足时,须要将一部分数据页淘汰掉,如果淘汰的是脏页,须要先将脏页同步到磁盘;
- MySQL 认为闲暇的工夫,这种状况没有性能问题;
- MySQL 失常敞开之前,会把所有的脏页刷入到磁盘,这种状况也没有性能问题。
在生产环境中,如果咱们开启了慢 SQL 监控,你会发现偶然会呈现一些用时稍长的 SQL。这是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。
LRU 淘汰策略
刚刚咱们理解了 InnoDB 的更新和插入操作的具体实现原理,接下来咱们再来理解下读的实现和优化形式。
InnoDB 存储引擎是基于汇合索引实现的数据存储,也就是除了索引列以及主键是存储在 B + 树之外,其它列数据也存储在 B + 树的叶子节点中。而这里的索引页和数据页都会缓存在缓冲池中,在查问数据时,只有在缓冲池中存在该数据,InnoDB 就不必每次都去磁盘中读取页,从而进步数据库的查问性能。
尽管缓冲池是一个很大的内存区域,但因为寄存了各种类型的数据,加上存储数据量之大,缓冲池无奈将所有的数据都存储在其中。因而,缓冲池须要通过 LRU 算法将最近且常常查问的数据缓存在其中,而不常查问的数据就淘汰进来。
InnoDB 对 LRU 做了一些优化,咱们相熟的 LRU 算法通常是将最近查问的数据放到 LRU 列表的首部,而 InnoDB 则是将数据放在一个 midpoint 地位,通常这个 midpoint 为列表长度的 5 /8。
这种策略次要是为了防止一些不常查问的操作忽然将热点数据淘汰进来,而热点数据被再次查问时,须要再次从磁盘中获取,从而影响数据库的查问性能。
如果咱们的热点数据比拟多,咱们能够通过调整 midpoint 值来减少热点数据的存储量,从而升高热点数据的淘汰率。
总结
明天我带你学习了 InnoDB 的实现和运行原理,心愿你能感触到 mysql 的 InnoDB 的设计思维,造就本人数据库性能优化的能力。
本文由 mdnice 多平台公布