章节目标
- 了解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 | root@MySQL-01 17:58: [(none)]> show global status like 'innodb_dblwr%'\G; |
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操作不允许