InnoDB
InnoDB
存储引擎是 OLTP
(联机事务处理系统) 应用中核心表地首选存储引擎。
1. 体系结构
1.1 后台线程
InnoDB
是多线程模型,其后台有多个不同的后台线程,负责处理不同的任务。
1.1.1 Master Thread
主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(Insert Buffer
)、Undo
页的回收等。
1.1.2 IO Thread0
InnoDB
大量使用了 AIO
(异步IO
)来处理写IO
请求,IO Thread
的工作主要是负责这些 IO
请求的回调处理。
1.1.3 Purge Thread
事务被提交后,其所使用的undo log
可能不再需要,因此需要 PurgeThread
来回收已经使用并分配的 undo
页。
undo log
是InnoDB MVCC
事务特性的重要组成部分,当我们对记录做了变更操作时就会产生undo
记录,undoo
记录默认被记录到系统表空间。
1.1.4 Page Cleaner Thread
其作用是脏页的刷新操作都放入到单独的线程中来完成。目的是减轻 Master Thread
的工作以及对于用户查询的阻塞。
1.2 内存
1.2.1 缓冲池
InnoDB
是基于磁盘存储的,并将其中的记录按照页的方式进行管理。CPU与磁盘速度有几个数量级的差距,而缓冲池技术则是用来弥补差距。
缓冲池的工作原理如下:
在数据库进行读取页的操作时,首先将磁盘读到页放在缓冲池中。下一次在读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。
在数据库进行修改页的操作时,首先修改缓冲池中的页,然后再以一定频率刷新到磁盘上。页从缓冲池刷新会磁盘的操作并不是在每次页发生更新时触发,而是通过一种CheckPoint
的机制刷新回磁盘。
缓冲池的数据页类型:
索引页、数据页、undo
页、插入缓冲(insert buffer)
、自适应哈希索引(adaptive hash index)
、InnoDB
存储的锁信息(lock info
)、数据字典信息(data dictionary
)等
1.2.2 LRU List、Free List 和 Flush List
数据库中的缓冲池是通过LRU
算法进行管理的,即最频繁使用的页也在LRU
列表的前端,而最少使用的页在LRU
列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU
列表中尾端的页。
InnoDB
中,LRU
列表中还加入了midpoint
位置。新访问的页并不是直接放到LRU
列表的首部,而是放到LRU
列表的midpoint
位置。这样做的原因是某些SQL
可能会导致将不活跃的数据刷入,而活跃被完全刷出。
LRU
列表用来管理已经读取的页,数据库刚启动时,LRU
列表是空的,即没有任何的页。此时页都存放在Free
列表中查看是否有可用的空闲页,若有则将该页从Free
列表中删除,放入到LRU
列表中。
LRU
列表中的页被修改后,称该页为脏页,即缓冲池中的页或磁盘上的页的数据产生了不一致。Flush
列表中的页即为脏页列表,脏页即存在于LRU
列表中,也存在与Flish
列表。Flush
列表用来管理将脏页刷新回磁盘。
1.2.3 重做日志缓冲
重做日志记录每次修改后的最新值,InnoDB
首先将重做日志信息放入到重做日志缓冲区,然后按一定频率将其刷新到重做日志文件。
重做日志缓冲中的内容会在以下三种情况下刷新到磁盘的重做日志文件中:
Master Thread
每一秒将重做日志缓冲刷新到重做日志文件;- 每个事务提交时会将日志缓冲重新到重做日志文件;
- 当重做日志缓冲池剩余空间小于
1/2
时,日志缓冲重新到重做日志文件;
1.2.4 额外的内存池
在对一些数据结构本身的内存进行分配时,需要重从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。
2. Checkpoint 技术
前面说过,为了提升效率,缓冲池中的页被修改后,并没有立即将脏页刷入磁盘,而是以一定的频率进行刷入。但是如果还没有刷入,此时发生宕机,那么数据就不能恢复。
为了解决该问题,当前事务数据库系统普遍都采用了 Write Ahead Log
策略,即当事务提交时,先写重做日志,在修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。
Checkpoint
技术的目的是解决以下几个问题:
- 缩短数据库的恢复时间;
发生宕机时,不需要重做所有日志,只需对Checkpoint
之后的重做日志进行恢复,这样大大缩短了恢复时间。Checkpoint
之前的页都已经刷入磁盘了。
- 缓冲池不够用时,将脏页刷新到磁盘;
缓冲池不够用时,将强制执行Checkpoint
.
- 重做日志不可用时,刷新脏页。
重做日志如果有不再需要的部分,则可以被覆盖重用。如果此时还需要使用重做日志,那么必须强制执行Checkpoint
,因此这部分就可以被覆盖重用。
2.1 Checkpoint工作原理
数据库实例崩溃时,内存中的DB_Buffer
中的修改过的数据,可能没有写入到磁盘中。数据库在重新打开时,需要进行恢复,来恢复DB Buffer
中的数据状态,并确保已经提交的数据被写入到磁盘中。通过它Checkpoint
,可以确定重做日志的哪一部分应该被扫描并应用于恢复。
Checkpoint
机制并不是直接将脏页刷回到磁盘的,而是通过double write
(两次写)。即通过memcpy
函数将脏页复制到内存中的doublewrite buffer
,之后通过double write
分两次(每次 1 MB
)顺序写入共享表空间的物理磁盘上,然后调用fsync
函数同步磁盘。
下文对于两次写以及数据恢复做了详细解释:
Checkpoint
分类:
- Sharp Checkpoint
Sharp Checkpoint
发生在数据库关闭时,将所有的脏页都刷新回磁盘。
- Fuzzy Checkpoint
Fuzzy Checkpoint
指刷新一部分脏页,而不是刷新所有的脏页回磁盘。
在以下几种情况会进行Fuzzy Checkpoint
:
- 每秒或每 10 秒的速度从缓冲池的脏页列表中刷新一定比例的页回到磁盘。
- 可用空闲页不足 100 个,
InnoDB
会将LRU
列表尾端的页移除。如果这些页中有脏页,需进行Checkpoint
。 - 重做日志不可用时,强制将一些页刷新回磁盘。
- 脏页数量太多。
3. InnoDB 关键特性
3.1 插入缓冲
对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若存在,则直接插入;若不在,则先放入到一个 Insert Buffer
对象中,假装已经插入。数据库这个非聚集索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行 Insert Buffer
和辅助索引页子节点的 merge(合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。
3.2 两次写
3.3 自适应哈希索引
InnoDB
会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。
自适应哈希索引对这个页的连续访问模式必须是一样的,访问模式指的是查询的条件一样。
WHERE a = xxx
WHERE a = xxx and b = xxx
若上述两种查询,交替进行上述查询,并不会对该页构造自适应哈希索引。
自适应哈希索引还有如下要求:
- 以该模式访问了 100 次
- 页通过该模式访问了 N 次,其中 N = 页中记录 * 1/16。
3.4 异步 IO
同步IO
指,每进行一次IO
操作,需要等待此次操作结束才能继续接下来的操作。
AIO
指用户可以在发出一个IO
请求后立即再发出另一个IO
请求,当全部IO
请求发送完毕后,等待所有IO
操作的完成。
3.5 刷新邻接页
刷新邻接页的工作原理是:当刷新一个脏页时,InnoDB
会检测该页所在的区的所有页,如果是脏页,那么一起进行刷新。这样做的好处是,通过AIO
可以将多个IO
写入操作合并为一个IO
操作。