InnoDB存储引擎——Buffer Pool缓冲池

概念

Buffer Pool 是 InnoDB 的一块内存区域,缓存数据行,索引,自适应哈希索引,插入缓存,锁,以及其他内部数据结构。
当我们执行数据查询的时候,会先去缓冲池中查找目标数据,如果找不到则读取磁盘中的数据页到缓冲池中,以缓存页的形式存储。
Buffer Pool缓冲池中每个缓存页都有一个描述信息,用来描述缓存页;
描述信息中包含:数据页所属的表空间,数据页编号,缓存页在Buffer Pool中的地址等信息
在这里插入图片描述

当数据库启动的时候,会根据设置的 innodb_buffer_pool_size大小,去操作系统中申请一块内存区域,作为缓冲池的内存区域,内存申请完成后按照默认缓存页16KB的大小和对应800字节的描述信息去划分内存区域。

Free 链表

当我们将数据页从磁盘加载到 Buffer Pool 中的缓存页中时,需要知道哪个缓存页是空的。
缓存池中维护了一个 Free链表,双向链表结构,来标记空闲的缓存页
在这里插入图片描述
在描述信息中,通过两个指针 free_pre,free_next 来维护 free 双向链表,一个 基础节点(40字节)引用链表的头节点和尾节点

数据页从磁盘加载到缓冲池的步骤

  • 从free链表中取一个空闲缓存页的描述信息,找到对应的缓存页
  • 将数据写入缓存页;表空间,数据页号等信息写入描述信息
  • 从free链表中移除这个描述信息

数据页是否已被缓存

通过一个哈希表记录已经缓存的数据页,key为表空间 + 数据页号,value为缓存页地址。
当我们查找数据的时候,先根据表空间+数据页号作为key去哈希表中看看数据页是否已经被缓存,如果找到了,则根据缓存页地址读取缓存页数据;如果没找到,则再去从磁盘中加载数据页
在这里插入图片描述

Flush 链表

Buffer Pool 维护了一个 flush 链表,来标记数据被更新过的缓存页(脏页),待后台线程将缓存页数据刷回磁盘。
在这里插入图片描述
在描述信息中,通过两个指针 flush_pre,flush_next 来维护 flush 双向链表,一个 基础节点引用链表的头节点和尾节点

  • 缓存页数据被修改后,将其加入 flush 链表
  • 缓存页数据被刷入到磁盘后,从 flush 链表中移除

LRU 链表

当Buffer Pool中不存在空闲缓存页时,需要通过 LRU 算法(最近最少使用)来淘汰一些缓存页,也就是将一些缓存页刷回磁盘,腾出一些空闲的缓存页来加载新的磁盘数据页。
在这里插入图片描述
Buffer Pool 中维护了一个 LRU 链表来进行缓存页淘汰处理,当我们从磁盘加载一个数据页到缓冲池的时候,就把这个缓存页对应的描述信息放到 LRU 链表表头,只要一有数据从磁盘读取到缓存页就会放到LRU链表表头,后续如果有缓存页发生更改,则将缓存页挪到表头去,这样表头一直都是最新的数据,表尾总是最近最少访问的数据。

这种简单的LRU算法会带来一个问题:
在这里插入图片描述
如图所示,MySQL 有预加载机制,当出现与加载或者全表加载的时候,会将部分只读1次的数据加载到链表表头,无法通过LRU及时清理这部分缓存页,导致经常被访问的缓存页跑到了链表尾部。

预读机制触发时机

  • innodb_read_ahead_threshold参数:默认值为56,如果顺序访问了一个区里的多个数据页,访问的数据页数量超过这个阈值,就会触发预读机制,把下一个相邻区的数据页都加载缓存中去
  • innodb_random_read_ahead参数:默认为OFF,如果Buffer Pool里缓存了一个区连续13个数据页,而这些数据页也是比较频繁被访问的,此时就会直接触发预读机制,把这个区的其他数据页都加载到缓存中去

MySQL设计这种预读机制主要是为了提高查询效率,减少磁盘IO。

针对上述问题,MySQL对LRU算法进行了优化,采用了冷热隔离的思想,将链表划分成了2部分,一部分存放热数据,一部分存放冷数据;通过参数innodb_old_blocks_pct 来设置分界点,默认值为37,表示37%的区域存冷数据,剩下63%的区域存热数据
在这里插入图片描述
LRU链表被拆分成2部分后,当数据页被加载到缓存时,首先把它加到冷数据区域链表的表头,经过 innodb_old_blocks_time (默认1000毫秒)时间后,又被访问到了,则将其加到热数据区域的链表表头中去。

如果此时缓存页不够用,会淘汰冷数据区域链表尾部的缓存页。

MySQL针对热数据区进行了一定的优化,在热数据区中,不是每个缓存页被访问后都会将其移动到热数据区链表表头,只有热数据区后3/4部分的缓存页被访问了,才会移动到表头,前1/4区域数据被访问了,不会移动到表头;减少了指针的移动

刷磁盘策略

  • 后台线程定时将LRU冷数据区链表尾部的缓存页刷入磁盘
  • 后台线程定时将flush链表数据刷入磁盘
  • Buffer Pool中没有空闲的缓存页后,触发LRU算法,将冷数据区链表尾部的缓存页刷入磁盘

总结

Free 链表,Flush 链表,LRU 链表整体运行效果:

  • 执行数据查询或更新时,数据加载到一个缓存页,Free 链表移除这个数据页, LRU 冷数据区域链表头部放入这个缓存页,如果一段时间内再次被访问,则将其移动到 LRU 热数据区链表头部
  • 如果修改了一个缓存页,将其加入到 Flush 链表,Flush 链表会记录这个脏页
  • 后台线程定时将 LRU 冷数据区域链表尾部的数据刷回磁盘,从 LRU 链表中移除,从 Flush 链表中移除(数据如果有变更,则Flush链表会记录),释放缓存页,并将缓存页放回到 Free 链表中
  • 后台线程定时将 Flush 链表的数据刷回磁盘,从 Flush 链表中移除,从LRU 链表中移除,释放缓存页,并将缓存页放回到 Free 链表中
  • 没有空闲的缓存页,通过 LRU 算法淘汰冷数据区链表尾部的数据,将其刷回磁盘,从 LRU 链表中移除,从 Flush 链表中移除,释放缓存页,并将缓存页放回到 Free 链表中

参考资料

  • 《MySQL技术内幕 InnoDB存储引擎 第二版》 by 姜承尧
  • 《儒猿技术窝》公众号专栏《从零开始带你成为MySQL实战优化高手》 by 救火队队长
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值