查看原文
其他

刁钻面试官!这题不会直接走人!MySQL如何管理和淘汰Buffer Pool ?

运维路书 运维路书
2024-11-05

最近,有粉丝留言说,面试时被问到“MySQL如何管理和淘汰Buffer Pool ?”没有回答上来

这个真不应该,今天就来讨论一下这个问题。


Buffer Pool 是MySQL内存结构中最核心地组件之一,地位非常重要,这个问题必须要搞清楚!


Buffer Pool 用途


Buffer Pool 是Innodb存储引擎的内存结构中的四大核心组件之一

主要作用:缓存数据和索引加速读请求,避免每次数据访问都进行磁盘IO


内存价格高,容量有限,无法比拟磁盘容量,

因此需要把最热的数据放到最近的地方,以最大限度的降低磁盘访问


Buffer Pool 以页为单位缓存数据,MySQL的页大小为16K

缓冲池的常用管理算法为LRU,OS 和 memcache都是用这种算法


传统的LRU算法


将最新加入到缓冲池中的页放到LRU的头部,从而最晚被淘汰。

分为两种情况:


01


需要用到的页已经在缓冲池中了,只需要将此页移动到LRU的头部,没有页被淘汰。



  缓冲池中缓存了以上5个页


  现在需要访问4号页,由于4号页已经在缓冲池中了,因此只需要将4号页移动到LRU的头部,尾部的页不会被淘汰。



02


页不在缓冲池中,需要将页加入到LRU的头部,从而尾部的页就被淘汰了。


假如需要访问的页号为50



  • 页号50 不在缓冲池当中


  • 页号50加入到LRU头部,尾部的20号页被淘汰

OS 和 Memcache 都在使用传统的LRU算法


MySQL的LRU算法


MySQL为了解决【预读失效】和【缓冲池污染】的问题对普通的LRU算法进行了优化


Q

什么是预读?

A

磁盘读取,并不是按需读取,而是按页读取。一次至少读取一页,如果未来要读取的数据就在页中,就能够省去后续的磁盘IO,提高效率。


预读失效:


预读,提前把页数据加载到了缓冲池中,但最终MySQL没有从这个页中读取数据,称为预读失效。


优化预读失效的思路:

  • 让预读失效的页留在缓冲池中的时间尽可能短

  • 真正被读取的页才移动到LRU的头部

实现方法:

    1. 将LRU分为两部分:

    • 新区(new block)

    • 旧区(old block)


  2. 新区旧区首尾相连,新区的尾部连接旧区的头部。

  3. 新页加入到缓冲池时,只会加入到旧区的头部


如果新页预读成功,就会移动到新区的头部;

如果新页预读失败,没有被真正使用,只会比新区的热数据更早的淘汰掉;

缓冲区污染:


当一个质量很差的sql做了全表扫描


例如:select * from user where name like %abc%;


此类sql无法命中索引,要进行全表扫描,需要访问大量的页


  1) 把页加载到缓冲池中(插入到旧区头部)


  2) 从页中读出相应的row(插入到新区头部)


  3) row里的name字段与字符串 abc进行比较,符合条件的放入到结果集中


  4) 直到扫描完所有页的所有行


如此一来,所有页都被加入到了新区的头部,但只会访问一次,大量真实的热数据被换出了缓冲池。


如何解决缓冲池污染的问题?


MySQL缓冲池增加了一个旧区停留时间的机制


  1)假设T=旧区停留时间窗口


  2)插入到旧区的头部,并且立刻被访问到,不会立即插入到新区的头部


  3)只有满足 "被访问" 且 "在旧区停留时间大于T" 才被插入到新区的头部


MySQL缓冲池相关的重要参数:


  innodb_buffer_pool_size:

      缓冲池大小,在内存允许的情况下尽量调大。

  innodb_old_blocks_pct:

  旧区在整个LRU链中占的比例,默认值为37。

      即 新区:旧区=63:37

  innodb_old_blocks_time:

  旧区的停留时间窗口设置,默认值为1000毫秒


总结



【以上仅为个人观点,如有不同意见,欢迎留言讨论!】



点击蓝字 关注我们







继续滑动看下一个
运维路书
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存