InnoDB体系架构之内存池
- 一、InnoDB 体系结构
- 二、缓冲池 buffer pool
- 内部结构
- free 链(管理空闲缓冲页)
- 怎么知道数据页是否被缓存?
- flush 链表(管理脏页)
- 1. 脏页
- 2. 链表结构
- 3. 刷盘时机
- LRU 链表(控制数据热度,淘汰策略)
一、InnoDB 体系结构
下图显示了 InnoDB 存储引擎的体系结构。
图中的内存池主要负责以下工作:
- 维护所有进程/线程需要访问的多个内部数据结构;
- 缓存磁盘上的数据,方便快速的读取,同时在对磁盘文件的数据修改之前在这里缓存。
- 重做日志(redo log)缓冲。
- …
图中的后台线程主要作用是负责刷新内存池中的数据,保证缓存池中的内存缓存是最近的数据。此外将已修改的数据文件刷新到磁盘文件中,同时保证在数据库中发生异常的情况下 InnoDB 能恢复到正常运行状态。(如:Master Thread:负责将缓存池中的数据异步刷新到磁盘;IO Thread:异步处理IO请求,提高数据库的性能)
二、缓冲池 buffer pool
InnoDB 存储引擎是基于磁盘进行存储的,并将其中的记录按照页的方式进行管理。由于【磁盘速度】和【内存速度】相比根本不值一提,所以一般基于磁盘的数据库系统通常使用缓冲技术来提高数据库的整体性能。
缓冲池简单来说就是一块内存区域。在数据库中进行读取页的操作,首先将磁盘中读到的页存放到缓冲池中,这个过程称为将页【FIX】在缓冲池中。下一次再读相同的页时,首先判断是否在缓冲池中,不再的话再读取磁盘中的页。
对于 InnoDB 存储引擎而言,其缓冲池的配置是通过参数 innodb_buffer_pool_size
来设置的。
my.cnf 下的配置
这个数据是字节数,转换一下就是256MB。
内部结构
整个 buffer pool 是由缓冲页和控制块组成的:
- 缓冲页:buffer pool 中存放的【数据页】称之为【缓冲页】,和磁盘上的数据页是一一对应的,都是16KB,缓冲页的数据,是从磁盘上加载到 buffer pool 当中的一个完整页。
- 控制块:他是缓冲页【描述信息】,这一块区域保存的是数据页所属的表空间号,数据页编号,数据页地址,以及一些链表相关的节点信息等,每个控制块大小是缓冲页的5%左右,大约是800个字节。
其内部结构如下,buffer pool 的前一部分存储【控制块】,后一部分存储【缓冲页】,如果中间有未被利用的空间,就叫他【内存碎片】吧。
buffer pool 的初始化:数据库会在启动的时候,安装配置中的 buffer pool 的大小,去向操作系统申请一块内存,作为 buffer pool 的内存区域,然后会按照默认的缓存页的大小【16KB】以及对应的【800字节左右】的【控制块】的大小,在buffer pool 中划分出一个一个的缓存页和一个一个与其对应的描述数据(控制块)。此时的buffer pool 像一个干净的本子,没有书写任何内容。
free 链(管理空闲缓冲页)
InnoDB 在设计之初,将所有的【空闲的缓冲页】所对应的【控制块】作为一个个的节点,形成一个链表,这个链表就是free链,翻译过来就是空闲链表。如下图所示:
由上图可知,free 链表是一个双向链表,链表上除了控制块以外,还有一个基础节点,存储了free 链由多少个描述信息块,也就是由多少个空闲的缓存页,以及指向链表头尾的指针。
当我们加载数据的时候,会从free链中找到空闲的缓存页,把数据页的【表空间和数据页】号写入【控制块】。
加载数据到缓存页后,会把缓存页对应的控制块从free链表中移除。
怎么知道数据页是否被缓存?
已经有了 free 链表用来【保存空闲的页】,但是,当下一次访问时,要如何知道当前要访问的页是不是已经被缓存了,最直接的思路就是将buffer poll 里面的缓存数据【全部遍历一遍】。显然不合理。
事实上,使用【表空间号+页号】即可确定唯一的页,那就可以直接设计一个【hash表】,使用【表空间号+页号】当做key,使用【控制块地址】做value,每次查询的时候只需要将key进行查找即可,时间复杂度为O(1),这样即可快速定位到缓存的页。
大致流程如下图所示:
flush 链表(管理脏页)
1. 脏页
在 SQL 的执行过程中,无论是增删改查,都是优先在 buffer pool 中进行的,这样可以极大的保障执行效率。但是同样会有一个问题,假如我们对缓存页中的某些数据进行了修改(执行了一条update语句),就会导致 buffer pool 中的缓冲页和磁盘中的数据页【数据不一致】,那么此时的缓冲页就称为【脏页】。当然,这也就说明了,脏页的数据是要刷到磁盘上的。
2. 链表结构
- flush 链表同样是一个双向链表,链表节点是被【修改过的缓冲页(脏页)】的控制块。
- 和 free 链表一样,flush 链表也有一个基础结点,链接首尾结点,并存储了有多少个控制块。
3. 刷盘时机
后台会有专门的线程每隔一段时间就把 flush 链表中的脏页刷入磁盘中,刷新的速率取决于当前系统是否繁忙。在这样的机制下,万一系统崩溃,是会产生数据不一致的问题的,没有刷入磁盘的数据就会丢失,而MySQL通过日志系统解决了这个问题。(undo log 记录的是数据操作前的样子redo log 记录的是数据被操作后的样子;redo log 是 Innodb 存储引擎特有;bin log 记录的是整个操作记录)
LRU 链表(控制数据热度,淘汰策略)
buffer pool 的内存是有限的,缓存只是数据的中转站,当数据量很大以后,buffer pool 只能容纳一部分数据,当需要更多的空间缓存【新的数据页】的时候,我们需要将最近最少使用的【缓冲页淘汰掉】,这就是【LRU淘汰算法】。对InnoDB而言,则是通过【LRU链表】来完成此功能的,结构和上面的 free、flush 基本一致,只是负责的功能不一样而已。
可以通过 show engine innodb status
语句来查看当前 InnoDB 的状态。