章节目标

  • 了解InnoDB存储引擎的历史
  • 了解InnoDB存储引擎的体系结构(包括后台线程和内存结构)
  • 了解InnoDB存储引擎的关键特性
  • 了解启动和关闭MySQL时一些文职文件参数对InnoDB存储引擎的影响

2.1 InnoDB存储引擎概述

  • 第一个完整支持ACID事务的MySQL存储引擎
  • 特点式行锁设计、支持MVCC、支持外键、提供一致性非锁定读
  • 高性能、高可用、高可扩展的存储引擎

2.2 InnoDB存储引擎的版本

2.3 InnoDB体系架构

InnoDB的存储引擎的体系架构:

  • 多个内存块组成了一个大的内存池负责以下工作:
    • 维护所有进程/线程需要访问的多个内存数据结构
    • 缓存磁盘上的数据,方便快速地获取,同时在对磁盘文件的数据修改之前在这里缓存
    • 重做日志缓冲
  • 后台线程负责以下工作:
    • 刷新内存池中的数据
    • 保证缓冲池中的内存缓存的是最近的数据
    • 将已修改的数据文件刷新到磁盘文件
    • 保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态

2.3.1 后台线程

InnoDB存储引擎是多线程的模型,后台有多个不同的后台线程。

1.Master Thread

  • 主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性。
  • 脏页的刷新、合并插入缓冲、UNDO页的回收等。

2.IO Thread

  • 主要负责IO请求的回调处理。(InnoDB存储引擎中大量使用了AIO来处理IO请求)
  • 共有4种IO Thread,分别是write、read、insert buffer和log IO thread。
    • 1.0版本共有4个,1.0.x版本开始write、read分别增大到了4个
    • 读线程的ID总是小于写线程

3.Purge Thread

  • 回收已经使用并分配的undo页
    • 1.1版本之前,purge操作仅在InnoDB存储引擎的Master Thread来完成。
    • 1.1版本开始,purge操作可以独立到单独的线程中进行,减轻Master Thread的工作,从而提高CPU的使用率以及提升存储引擎的性能。
    • 1.2版本开始,InnoDB支持多个Purge Thread,这样做的目的是为了进一步加快undo页的回收。
      • 离散地去读undo页,利用磁盘的随机读取性能。

4.Page cleaner Thread

  • 将之前版本中的脏页刷新操作都放入到单独的线程中完成
  • 减轻原Master Thread的工作及对于用户查询线程的阻塞

2.3.2 内存

1.缓冲池

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。

缓冲池就是一块内存区域,通过内存的速度来民不磁盘速度较慢对数据库性能的影响。

  • 在数据库中进行读取页的操作
    • 首先将从磁盘读到的页存放在缓冲池中
    • 下一次再读相同的页时,首先判断该页是否在缓冲池中。
    • 若再缓冲池中,称该页再缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。
  • 在数据库中页的修改操作
    • 首先修改在缓冲池中的页
    • 再以一定的频率刷新到磁盘上
    • 页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为Checkpoint的机制刷新回磁盘。

缓冲池的大小直接影响这数据库的整体性能。

缓冲池中缓存的数据页类型有:

  • 索引页
  • 数据页
  • undo页
  • 插入缓冲
  • 自适应哈希索引
  • InnoDB存储的锁信息
  • 数据字典信息

2.LRU List、Free List和Flush List

数据库中的缓冲池是通过LRU(Latest Recent Used,最近最少使用)算法来进行管理的。

最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端。

  • InnoDB对传统的LRU算法做了一些优化
    • 加入了midpoint位置,该位置在LRU列表长度的5/8处
    • 新读取的页插入到LRU列表尾端的37%的位置(innodb_old_blocks_pct)
    • midpoint之后的列表称为old列表,之前的列表称为new列表
    • 页读取到mid位置后需要等待多久才会被加入到LRU列表的热端(innodb_old_blocks_time)

3.重做日志缓冲

InnoDB存储引擎:

  • 首先将重做日志信息先放入到缓冲区
  • 按一定频率将其刷新到重做日志文件
  • 一般情况下每一秒钟会将重做日志缓冲刷新到日志文件
  • 只需要保证每秒产生的事务量在这个缓冲大小之内即可

重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中:

  • Master Thread 每一秒将重做日志缓冲刷新到重做日志文件
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件
  • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件

4.额外的内存池

申请了很大的Innodb缓冲池时,也应考虑相应地增加这个值。

2.4 Checkpoint技术

Checkpoint技术的目的时解决以下几个问题:

  • 缩短数据库的恢复时间
  • 缓冲池不够用时,将脏页刷新到磁盘
  • 重做日志不可用时,刷新脏页

两种Checkpoint:

  • Sharp Checkpoint

    • 发生数据库关闭时将所有的脏页都刷新回磁盘,innodb_fast_shutdown=1
    • 数据库运行时使用,数据库可用性会收到很大的影响
  • Fuzzy Checkpoint

    • 刷新一部分脏页
    • Master Thread Checkpoint
    • FLUSH_LRU_LIST Checkpoint
    • Async/Sync Flush Checkpoint
    • Dirty Page too much Checkpoint

2.5 Master Thread 工作方式

2.5.1 InnoDB1.0.X 版本之前的Master Thread

Master Thread具有最高的线程优先级别

其内部由多个循环(loop)组成:主循环(loop)、后台循环(backgroup loop)、刷新循环(flush loop)、暂停循环(suspend loop)

主循环(loop)

每秒一次的操作包括

  • 日志缓冲刷新到磁盘,及时这个事务还没有提交(总是)
  • 合并插入缓冲(可能)
  • 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)
  • 如果当前没有用户活动,则切换到backgroup loop(可能)

即使某个事务还没有提交,InnoDB存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。

合并插入缓冲并不是每秒都会发生的。会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插入缓冲的操作。

每10秒的操作包括

  • 刷新100个脏页到磁盘(可能的情况下)
  • 合并至多5个插入缓冲(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或者10个脏页到磁盘(总是)
    • 超过70%的脏页,刷新100个脏页到磁盘
    • 小于70%,只需刷新10%的脏页到磁盘

会判断过去10秒之内磁盘的IO操作是否小于200次,如果是,认为有足够磁盘IO操作能力,因此将100个脏页刷新到磁盘。接着,InnoDB存储引擎会合并插入缓冲

后台循环(backgroup loop)

若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown),就会切换到这个循环

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

2.5.2 InnoDB1.2.x版本之前的Master Thread

  • innodb_io_capacity,表示磁盘IO的吞吐量,默认值为200
  • 刷新到磁盘页的数量,会按照innodb_io_capacity的百分比来进行控制
    • 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%
    • 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_capacity
  • innodb_adaptive_flushing(自适应刷新)
    • 通过判断产生重做日志(redo log)的速度来决定最合适的刷新脏页数量。
  • innodb_purge_batch_size(动态),控制最多回收undo页数量

2.5.3 InnoDB1.2.x版本的Master Thread

刷新脏页的操作,从Master Thread线程分离到一个单独的Page Cleaner Thread,提高了系统的并发性。

2.6 InnoDB关键特性

  • 插入缓冲(Insert Buffer)
  • 两次写(Double Wirte)
  • 自适应哈希索引(Adaptive Hash Index)
  • 异步IO(Async IO)
  • 刷新邻接页(Flush Neighbor Page)

2.6.1 插入缓冲

1.Insert Buffer

  • 索引是辅助索引(secondary index)
  • 索引不是唯一(unique)的

2.Change Buffer

对一条记录进行update操作可能分为两个过程:

  • 将记录标记为已删除
  • 真正将记录删除

3.Insert Buffer的内部实现

【待完善,没弄明白】

4.Merge Insert Buffer

  • 辅助索引页被读取到缓冲池时
  • Insert Buffer Bitmap页最总到该辅助索引页已无可用空间时
  • Master Thread

2.6.2 两次写

在应用重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite

  • innodb_dblwr_pages_written 统计生产环境写入的量
1
2
3
4
5
6
7
8
9
10
root@MySQL-01 17:58:  [(none)]> show global status like 'innodb_dblwr%'\G;
*************************** 1. row ***************************
Variable_name: Innodb_dblwr_pages_written
Value: 831
*************************** 2. row ***************************
Variable_name: Innodb_dblwr_writes
Value: 294
2 rows in set (0.18 sec)

--Innodb_dblwr_pages_written:Innodb_dblwr_writes远小于64:1,说明系统写入压力并不是很高。

2.6.3 自适应哈希索引

B+树的高度一般为34层,故需要34次的查询

2.6.4 异步IO

  • innodb_use_native_aio控制Native AIO是否启用
  • Mac OSX系统不提供Native AIO
  • 内核级别AIO的支持,需要libaio库的支持

2.6.5 刷新邻接页

  • 当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果时脏页,那么一起进行刷新。
  • innodb_flush_neighbors 控制是否启用该特性
  • 固态硬盘超高IOPS性能,建议关闭此特性

2.7 启动、关闭与恢复

  • innodb_fast_shutdown
    • 0表示完成所有的full purge 和merge insert buffer,并且将所有的脏页刷新回磁盘。
    • 1默认值,不需要完成full purge 和merge insert buffer,一些数据脏页还是会刷新回磁盘
    • 2不完成full purge 和merge insert buffer,不将数据脏页还是回刷新回磁盘,而是将日志写入日志文件,下次启动时会进行恢复操作
  • innodb_force_recovery
    • 大于0后,用户可以对表进行select、create、drop操作,但是insert、update、delete这类DML操作不允许