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