InnoDB存储引擎内幕
一、InnoDB存储引擎
1、体系架构
1.1. 后台线程
1.1.1. Master Thread
该线程是InnoDB存储引擎的核心线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(Insert Buffer)、UNDO页的回收等
1.1.2. IO Thread
在InnoDB存储引擎中使用了大量的AIO(Async IO)来处理写IO请求,这样可以大大提高数据库的性能。而 IO Thread的工作主要就是负责这些io请求的回调处理(call back)。 IO Thread有四个类型,分别是
- write thread
- read thread
- insert buffer thread
- log thread
使用以下命令来观察:
show engine innodb status\G;
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
可以使用innodb_read_io_thread和innodb_write_io_thread来对线程数量进行设置
1.1.3. Purge Thread
事务被提交后,其所使用的undolog可能不在需要,因此需要Purge Thread来回收已经使用并分配的undo页。在InnoDB1.1版本之前purge 是在Master Thread线程中完成的。可以通过如下命令来启用purge配置:
[mysqld]
innodb_purge_thread=1
1.1.4. Page Cleaner Thread
将之前版本中脏页的刷新操作都放入到单独的线程中来完成
1.2. 内存
1.2.1. 缓冲池
在我们深入了解缓冲之前,我们先来看一下以下几个问题:
- a、我们的明白我们为什么要在InnoDb存储引擎中使用缓冲池技术?
- b、使用缓冲池技术有哪些好处?
- c、什么是缓冲池技术?
因为InnoDB是基于磁盘存储的,并将其中的数据按照页的方式进行管理,因此可将其视为基于磁盘的数据库系统。在数据库系统中由于CPU的速度与磁盘速度之间存在巨大差异,所以基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能
缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库新能的影响。因此缓冲池的大小直接影响数据库的整体性能,可以通过innodb_buffer_pool_size来设置,通过如下命令来查看:
show variables like `innodb_buffer_pool_size`\G;
具体来看缓冲池中缓存的数据页类型有:
- 数据页
- 索引页
- undo页
- 插入缓冲(insert buffer)
- 自适应哈希索引
- InnoDB存储的锁信息(lock info)
- 数据字典信息
1.2.2. LRU List、Free List 和 Flush List
问题: 我们知道了缓冲池是一个很大的内存区域,其中存放了各种类型的页,那么InnoDB是怎么对这么大的内存区域进行管理的了?
针对以上问题,我们来看一下LRU算法,因为数据库的缓冲池正是通过LRU(最近最少使用)算法来管理的。即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU的尾端。当LRU不能存放新读取到的页时将首先释放LRU列表中尾端的页,我们开看一下具体流程:
这里用到了顺序表list来作为缓冲池,每个数据节点称为block
该算法采用“中点插入法”:当插入一个新block时,移除表尾最近最少使用的block,在中点插入新block。
这个中点将链表分为两部分:
- 1.靠近表头的一部分,为young区,这里的block是最近使用的节点
- 2.靠近表尾的一部分,为old区,这里的block是最近少使用的
该算法通过链表中的block的使用热度来维持各block的位置,其中old区的block为链表满的时候移除的候选区
具体算法如下:
- 1.链表的3/8被设置为old区
- 2.中点不是链表的中间点,而是old区的表头节点,即old区与young区的相邻的那个节点
- 3.当读取的数据不在缓冲池里的时候,读取到的block需要插入到链表中,插入点为中点,但是插入的新节点为old区的节点,如果此时old区满了得话,移除表尾的block(LRU节点)
- 4.当读取old区的block时,该节点将变成“young”节点:此节点移动到young区的表头(young区的头部那里)
- 5.在数据库操作中,被访问的节点将移除到young的表头,这样一来,在young区中的未被访问的节点将逐渐往表尾移动,当移动过中点,将变为old区的节点。而old区的节点若被访问到将变为young节点移动到表头,而old区中的为被访问的节点依旧往表尾移动,当表满时,表尾那个block将会被淘汰掉
脏页:缓冲池中的页和磁盘中的页的数据产生的不一致
Flush List:用来管理将脏页刷新回磁盘,和lru list 互不影响
LRU List: 用来管理缓冲池中页的可用性
注意:脏页即存于Flush List ,也存于LRU List中
1.2.3. 重做日志缓冲
InnoDB存储引擎首先将重做日志放入这个缓冲区,然后按一定的频率刷新到重做日志文件,重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中:
- Master Thread每一秒将重做日志缓冲刷新到重做日志文件
- 每个事务提交时会将重做日志缓冲刷新到重做日志文件
- 当重做日志缓冲池剩余的空间小鱼1/2时,重做日志缓冲刷新到重做日志文件
1.2.4. 额外的内存池