1. InnoDB存储引擎体系架构


内存块

  • 维护所有进程/线程需要访问的多个内部数据结构
  • 缓存磁盘的数据,方便快速的读取,并在磁盘数据修改之前在这里缓存
  • 重做日志缓冲

后台线程

  • 刷新内存池的数据,保证数据是最新的
  • 将已经修改的数据文件保存到磁盘中
  • 保证数据库在发生异常的情况下InnoDB能恢复到正常的状态
  • 主要工作在master thread中完成

内存

  • 内存由缓冲池重做日志缓冲池额外的内存池组成
  • 缓冲池是占内存最多的部分,缓存的数据类型有:索引页数据页undo页插入缓冲自适应哈希索引、存储的锁信息、数据字典信息等

InnoDB存储引擎工作方式

  • 将数据库文件按页(每页16k)读取到缓冲池,然后按照最近最少使用的算法(LRU)保留缓存数据。
  • 如果数据发生更改,总是先修改缓存池的页(脏页),然后再保存在磁盘中

日志缓冲

  • 保证持久性。即:一旦事务提交, 则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
  • 将重做日志先放入缓冲区,然后保存在重做日志文件

额外的内存池

  • 当对一些数据结构分配内存时,从额外的内存池中申请,因为缓冲池中的数据都是有意义的

2 master thread

2.1 源码分析

  • 线程优先级别最高。
  • 由几个循环组成:主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环(suspend loop)。

2.1.1 主循环(loop)

伪代码:

  • 大部分操作在这个里面进行,分为两大部分:每秒操作每十秒操作
  • 在负载很大时,可能会有延迟(delay),每秒操作和每十秒操作可能不准确

每秒一次的操作

  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并插入缓存(更新索引)
  • 至多刷新100个InnoDB的缓冲池中的脏页数据到磁盘(可能)
  • 如果当前没有用户活动,切换到后台循环(可能)

综上代码优化为
每十秒一次的操作

  • 刷新100脏页到磁盘(可能)
  • 合并至多5个插入缓存(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页–在操作任何数据之前,首先将数据备份到一个地方(总是)
  • 刷新100或者10%脏页到磁盘(总是)
  • 产生一个检查点–记录哪些日志在数据恢复时需要执行,哪些已经不需要执行


伪代码如下:

2.1.2 background loop

  • 数据库没有用户操作时或者数据库关闭时,就会切换到这个循环

执行的操作

  • 删除无用的Undo页(总是)
  • 合并20个插入缓存(总是)
  • 跳回主循环(总是)
  • 不断刷新100个页,直到符合条件(可能,跳flush loop中完成)

伪代码

  • 当flush loop没有事情可以做,会切换到suspend loop暂时挂起

2.2 master thread潜在的问题

  • 发现InnoDB对IO是有限制的,在缓冲池向磁盘刷新有一定限制
  • 如果在密集的写的应用程序中,每秒产生超过100脏页或者超过20个插入语缓存,master thread会忙不过来

解决

  • 合并缓存的数量为innodb_ io_ capacity数值的5%。
  • 刷新脏页的数目可以让用户根据磁盘情况设置innodb_ io_ capacity

最终修复后的伪代码

3. 关键特性

  • 插入缓存–性能
  • 两次写–可靠性
  • 自适应哈希索引

3.1 插入缓存

  • 因为主键是表唯一标识,所以插入顺序按照主键递增(自增主键)的顺序插入。
  • 因此,插入的聚集索引一般是顺序的,不需要对磁盘随机读取,所以速度很快。
  • 但是一个表不止有聚集索引,索引的插入不再是顺序的

聚集索引:
索引中键值的逻辑顺序决定了表中相应行的物理顺序(索引中的数据物理存放地址和索引的顺序是一致的)

非聚集索引:
索引的逻辑顺序与磁盘上的物理存储顺序不同。

  • 插入索引对于非聚集索引,不是一次性插入到索引页,先判断索引页是否在缓存池。如果在,直接插入;如果不在,先放入插入缓存,将多个插入合并在一个中(因为都是在一个索引页中),在根据磁盘IO情况更新到磁盘中。
  • 索引必须是辅助索引,索引不是唯一的
  • 默认最多占一半缓存池空间

缺点

  • 由于并没有及时把索引更新到磁盘中,如果数据库宕机,则需要很多的时间恢复数据

3.2 两次写

  • 当数据库宕机时,数据库可能正在写一个页面,而这个页面只写了一部分,则称之为部分写失效,从而导致数据丢失
  • 如果此时直接使用Undo日志,由于页出现了损坏,所以此时是无意的
  • 在执行Undo日志之前,先需要一个页副本用来恢复的没有写之前的状态,再进行重做。
  • doublewrite由两部分组成:内存中的doublewrite buffer,物理磁盘共享表中的两个区
  • 在缓冲池脏页刷新时,先将数据拷贝到内存中的doublewrite buffer,然后在写入物理磁盘共享表中的两个区,然后在更新磁盘数据
  • 由于doublewrite是连续的,所以对其的IO操作时顺序写的,开销不大

3.3 自适应哈希索引

  • 哈希是一种查找办法,常用于join连接操作
  • 会监控表上索引的查找,如果建立哈希索引可以提供速度,则建立哈希索引。
  • 哈希索引通过缓存池中的B+数构造而来,因此建立速度很快
  • 并不是整个表都需要建立哈希索引,InnoDB会根据访问的频率为某些页单独建立哈希索引

4. InnoDB Plugin

  • 快速的索引重建
  • 更好的多核性能
  • 新的页结构
  • 页压缩能力
  • 更好的BLOB处理能力