一、InnoDB存储引擎概述
- InnoDB 是事务安全的 MySQL存储引擎。
- 支持行锁、MVCC、外键
- 提供一致性非锁定读
InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。
因此可将其视为基于磁盘的数据库系统(Disk-base Database)。
二、体系架构
InnoDB 存储引擎有多个内存块,可以认为这些内存组成了一个大的内存池,负责如下工作:
- 维护所有进程 / 线程 需要访问的多个内部数据结构;
- 缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存;
- 重做(redo log)缓冲。
…
后台线程
后台线程主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。
此外,将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下 InnoDB 能恢复到正常的运行状态。
内存
缓冲池
缓冲池是一块内存区域,存放各种类型的页。
使用缓冲池技术来通过内存的速度去弥补磁盘速度较慢对数据库性能的影响。
配置参数:innodb_buffer_pool_size
可以允许多个缓冲池实例,每个页根据哈希值平均分配到不同的缓冲池实例中。
作用:减少数据库内部资源竞争,提高并发处理能力。
配置参数:innodb_buffer_pool_instances
在数据库中进行读取页的操作,首先会从磁盘中读取的页放到缓冲池中,下次读取相同页时,看缓冲池是否能命中,能就直接读取,查询操作和修改操作的流程如图3 和图4所示。
LRU列表、Free列表、Flush列表
LRU(Lastest Recent Used,最近最少使用)
通常,数据库的缓冲池用 LRU 算法来管理。
概念:频繁使用的页放在 LRU列表 的前端,最少使用的页放在尾端。
作用:LRU列表用来管理已读取的页。
较传统的 LRU 做出相应优化:
-
LRU列表 加入
midpoint
位置(默认在LRU列表长度的 5/8 处),新读的页放在该位置。
midpoint 之后称old列表
(新读取的页放这),midpoint 之前称new列表
(热点数据)。
配置参数:innodb_old_blocks_pct
,默认值37,即 old列表的长度为 37%(约 3/8)。 -
加入
innodb_old_blocks_time
参数
当页读取到 midpoint 位置后,需要等待多久时间才加入到 LRU列表的热端(new列表),默认值1000,即1秒。
当页从 LRU 列表 的 old 部分加入到 new 部分时,此时操作称 page made young
;
因innodb_old_blocks_time
参数的设置而导致页无法 old -> new,则称 page not made young
注意:插入缓冲、锁信息、自适应哈希索引 等页不存在 LRU列表中。
Free列表
空闲列表。下图6,当需要从缓存池中分页时,分页的过程。
需要注意的是,当 Free列表中还有空闲页,则说明 LRU列表中页数未固定,还可以从 Free列表中取。
所以此时 midpoint 是动态变化的(如上文提到的,因为 midpoint 位置是按 LRU列表长度百分比进行设置的)。
反之,当 Free列表用完,LRU列表页数固定,则 midpoint 也会固定下来。
Flush列表
Flush列表即脏页列表。
LRU列表中页被修改后,称该页为脏页,即缓冲池和磁盘上的页的数据产生了不一致,缓冲池中的页的版本比磁盘的新。
这时数据库会通过 checkpoint 机制将脏页刷回磁盘。
LRU列表用来管理缓冲池中页的可用性,
Flush列表用来管理将页刷回磁盘。
注意:脏页存在于 LRU列表中,也存在于 Flush列表中。
重做日志缓冲
如上图3所示,InnoDB存储引擎的内存区域除了有缓冲池外,还有重做日志缓冲(redo_log_buffer)和 额外的内存池(innodb_additional_mem_pool_size)。
InnoDB存储引擎首先会把重做日志信息先放到这个缓冲区中,然后再按一定的频率刷到重做日志文件。
刷新频率一般为一秒,所以该区一般不用设置太大,用户只需保证每秒产生的事务量在这个缓冲大小之内即可。
配置参数:innodb_log_buffer_size,默认8MB。
重做日志缓冲中的内容 刷新到 外部磁盘的重做日志文件,有三种情况会触发。
- Master Thread 每秒将重做日志缓冲刷新到重做日志文件;
- 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
- 当重做日志缓冲池剩余空间小于 1/2 时,重做日志缓冲刷新到重做日志文件。
额外的内存池
在 InnoDB 存储引擎中,对内存的管理是通过一种成为内存堆(heap)的方式进行的。
在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。
每个缓冲池中的帧缓冲(frame buffer)和对应的缓冲控制对象(buffer control block),这些对象记录了一些诸如 LRU、锁、等待等信息,这些对象的内存需要从额外的内存池中进行申请。
因此,在申请了很大的 InnoDB 缓冲池时,也应考虑相应地增加这个值。
参考资料
[1] MySQL技术内幕 InnoDB存储引擎 第2版