刁钻面试官!这题不会直接走人!MySQL如何管理和淘汰Buffer Pool ?
最近,有粉丝留言说,面试时被问到“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毫秒
总结
【以上仅为个人观点,如有不同意见,欢迎留言讨论!】
点击蓝字 关注我们