墨星写作网站app下载,网络舆情监测关键词,永嘉网站开发公司,泉州网站建设qzdzi文章目录 一、空闲链表的管理二、缓冲页的哈希处理三、Flush链表的管理四、LRU链表的管理五、脏页刷新六、多Buffer Pool实例 InnoDB存储引擎是基于磁盘存储的#xff0c;并将其中的记录按照页的方式进行管理。在数据库系统中#xff0c;由于CPU速度与磁盘速度之间的鸿沟并将其中的记录按照页的方式进行管理。在数据库系统中由于CPU速度与磁盘速度之间的鸿沟基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。在数据库中进行读取页的操作首先将从磁盘读到的页存放在缓冲池中这个过程称为将页“FIX”在缓冲池中在下一次读取相同的页时首先判断该页是否存在缓冲池中如果存在则被命中直接读取否则读取磁盘上的页。
Buffer Pool对应的一片连续的内存被划分为若干个页面页面大小和InnoDB表空间使用的页面大小一致默认都是16KB。为了更好地管理Buffer Pool中的这些缓冲页InnoDB为每个缓冲页都创建了一个控制块其中包含该页所属的表空间编号、页号、缓冲页在Buffer Pool中的地址、链表节点等信息。控制块位于Buffer Pool的内存的前部分缓冲页位于Buffer Pool的后部分两部分之间可能会存在碎片如果剩余的空间不足够再分配一堆控制块和缓冲页则会产生。
一、空闲链表的管理
在我们最开始启动MySQL服务器的时候需要向操作系统申请Buffer Pool的内存空间然后把它换分成若干对控制块和缓冲页。当然此时没有真实的磁盘页被缓存到Buffer Pool中会随着程序的运行不断缓存。
当从磁盘上读取到一个页时我们需要将其放置在空闲的缓冲页中。那我们就需要区分哪些页是空闲的哪些页已经被使用了因此引入了Free链表空闲链表。在Buffer Pool刚刚完成初始化之后所有缓冲页对应的控制块都会加入到空闲链表中。而为了管理这个空闲链表我们为其定义了一个基节点其中包含了链表的头节点地址、尾节点地址以及当前链表中节点的数量等信息。需要注意的是链表的基节点占用的内存空间并不包含在为Buffer Pool申请的一大片连续内存孔内而是一块单独申请的内存空间。
有了空闲链表之后每当需要从磁盘中加载一个页到Buffer Pool中时就从空闲链表中取一个缓冲页对应的控制块把页加载到控制块指向的缓冲页中并把该控制块的信息给填上即表空间、页号等。
二、缓冲页的哈希处理
使用了Buffer Pool之后我们访问某个页的数据时会先去缓冲池中查找该页是否已经加载到内存中如果不存在才会去磁盘中加载。而为了快速判断目标页是否存在于缓冲池中InnoDB采用的是哈希表的方式将表空间号和页号作为键缓冲页控制块作为值来进行构建。
三、Flush链表的管理
如果我们修改了Buffer Pool中某个缓冲页的数据那其中的数据就和磁盘中的不同了此时这个缓冲页变为了脏页。虽然我们可以在每次修改完都将其刷新到磁盘中对应的页上但是这显然会严重影响程序的性能。因此InnoDB不会在每次修改完页都立刻刷库而是在未来某个时间点进行刷新。
既然不是及时刷新那我们在未来刷新时便需要知道哪些页是脏页。因此引入了Flush链表进行脏页的管理当缓冲页被修改则将其控制块加入到链表中。
和空闲链表相同它也有一个相同结构的基节点来保存这个链表的信息。显然当一个缓冲页的控制块在空闲链表中时则不可能在Flush链表中反之同理。
四、LRU链表的管理
Buffer Pool对应的内存大小毕竟是有限的随着程序的运行Free链表总归会用完。如果Free链表已经没有多余的空闲缓冲页了那么只能将某些旧的缓冲页从Buffer Pool中淘汰再将新的页放进来。Buffer Pool引入的初衷是为了想减少磁盘IO因此自然希望淘汰掉的页面是最少使用的而最近经常使用的页面不被淘汰因此InnoDB采用的淘汰策略为LRU即最近最少使用算法。以这个算法为核心InnoDB维护了一个LRU链表如果页不在Buffer Pool中在把该页从磁盘加载到Buffer Pool的时候就把该缓冲页对应的控制块作为节点添加到LRU链表的头部如果使用到链表中的某个页时也将其移动到链表头部。之后当空闲缓冲页用完时就从链表尾部进行淘汰。
但这种实现并不完善它存在两个问题 因为InnoDB提供了预读的功能即InnoDB认为执行当前的请求时可能会在后面读取某些页面于是就预先把这些页面加载到Buffer Pool中。根据触发方式的不同预读可分为以下两种 线性预读InnoDB中有一个系统变量innodb_read_ahead_threshold如果顺序访问某个区的页面超过这个系统变量的值则会触发一次异步读取下一个区中所有页面的请求随机预读如果某个区的13个连续的页面都被加载到了Buffer Pool中无论这些页面是不是顺序读取的都会触发一次异步读取本区中所有其他页面到Buffer Pool中的请求。这个功能默认不会开启预读到Buffer Pool中的页如果能够被成功的使用到那自然可以极大地提高语句执行的效率。但是如果用不到会导致LRU链表尾部的一些缓冲页很快就被淘汰掉从而大大降低Buffer Pool的命中率 有的时候可能需要用到全表扫描这个过程中需要访问的页面一般会特别多那么就会导致LRU链表中很多页面都会被淘汰掉。但是这次全表扫描引进来的这些页面可能并不是热点数据但是反而把热点数据从链表中赶跑了
为了避免以上两种情况把Buffer Pool中真正的热点数据淘汰InnoDB将LRU分成两个部分
一部分存储使用频率非常高的缓冲页称为热数据或young数据一部分存储使用频率不是很高的缓冲页称为冷数据或old数据
LRU的划分是根据比例的默认情况下old区域占LRU链表的37%。之后当磁盘的页面被初次加载到Buffer Pool中时该缓冲页对应的控制块会放置到old区域的头部而不再是整个链表的头部这样就不会使得预读加载进来的页影响young区域中使用比较频繁的缓冲页。并且在对某个处于old区域的缓冲页进行第一次访问时会在它对应的控制块中记录下整个访问时间如果后续的访问时间与第一次访问的时间在某个时间间隔内那么该页面就不会从old区域移动到young区域头部。这样一来也可以防止全表扫描时除了将数据页加载到Buffer Pool还会频繁读取页面中的记录导致仍然会淘汰掉热点数据的情况。
除了以上的改进LRU还有进一步的优化策略。对于young区域的缓冲页来说如果我们每次访问一个缓冲页都需要把它移动到LRU链表的头部其实开销是比较大的因为young区域都是热点数据。因此InnoDB规定只有被访问的缓冲页位于young区域1/4的后面时才会被移动到LRU链表的头部。
五、脏页刷新
后台有专门的线程负责每隔一段时间就把脏页刷新到磁盘这样可以不影响用户线程处理正常的请求刷新的方式主要有两种
从LRU链表的冷数据中刷新一部分页面到磁盘后台线程会定时从LRU链表尾部开始扫描一些页面。如果在LRU链表中发现脏页缓冲页的控制块就会记录该缓冲页是否被修改则把它们刷新到磁盘从Flsuh链表中刷新一部分到磁盘后台线程也会定时从Flush链表中刷新一部分页面到磁盘刷新的速率取决于当时系统是否繁忙
当有线程在准备加载一个磁盘页到Buffer Pool中时没有可用的缓冲页时会尝试查看LRU链表尾部看是否存在可以直接释放掉的未修改缓冲页。有时后台线程刷新脏页的进度比较慢导致LRU链表尾部的脏页还没有被刷新此时则不得不将LRU链表尾部的一个脏页同步刷新到磁盘中这会降低用户请求的速度。
六、多Buffer Pool实例
在多线程环境下访问Buffer Pool中的各个链表都需要加锁处理。在Buffer Pool特别大并且多线程并发量特别高的情况下单一的Buffer Pool可能会影响请求的处理速度。所以在Buffer Pool特别大时可以把它们拆分成若干个小的Buffer Pool每个Buffer Pool都称为一个实例。它们之间是相互独立的多线程并发时不会互相影响。