一文详解InnoDB最核心组件Buffer Pool(三)

前面笔者用了两篇文章,讲解InnoDB最核心组件Buffer Pool的部分知识点,对Buffer Pool的内部结构有了一定的了解。

第一讲主要引入了缓存页的概念。

一文详解InnoDB最核心组件Buffer Pool(一)

第二讲主要引入了三个链表:free链表、flush链表、lru链表。

一文详解InnoDB最核心组件Buffer Pool(二)

现在我们明白,当你执行一个CRUD操作时,InnoDB都会数据从磁盘上的数据页加载到缓存页里来。加载的时候,先从free链表找到一个空闲的缓存页,然后把磁盘上的数据页加载到那个空闲的缓存页里去。如果free链表没有空闲的缓存页了,可以去LRU链表尾部找到最近最少使用的缓存页,把它刷入磁盘,腾出空闲的缓存页,然后加载需要的磁盘数据页到空闲缓存页里去。

有的同学看了文章,可能会觉得lru链表这块,怎么跟我之前看过技术博客讲的不太一样?是不是作者讲错了?

其实不是的,通常分享一项技术,尤其是比较复杂的技术,都是由浅入深的,先介绍简单点的,慢慢介绍更深入的原理。不可能一上来把所有核心知识点一股脑全扔给你。

预读

LRU链表的机制很简单,只要没有空闲缓存页了,就从链表尾部淘汰一些缓存页,把新加载的缓存页放到LRU链表头部。

可是这样的运作机制,会有很大的隐患。

首先就是预读机制!

预读,就是当你从磁盘上加载一个数据页的时候,它可能会连带着把这个数据页相邻的其他数据页,都加载到缓存里去。

设计者这样这样设计,是考虑到当我们访问一个缓存页的时候,很可能会继续访问它相邻的其他缓存页的数据。

但是呢,有时候只有一个缓存页被访问了,其他被加载的缓存页访问一次后,就再也没访问了,此时这些预读的缓存页都在LRU链表的最前面。

图片

如图1所示,前两个缓存页是刚加载进来的,但第二个缓存页是预读连带加载进来的,它也被放在链表的头部,放在之前加载的缓存页的前面,但并没有人访问它。

此时如果没有空闲缓存页了,就需要从LRU链表尾部淘汰一些缓存页。但是,如果你把图1尾部的两个缓存页清空了,你觉得合理吗?它可是之前一直被访问的缓存页呢,只不过被新加载进来的缓存页给挤到尾部了。

这时候,你要是把LRU链表尾部的缓存页刷入磁盘,肯定是不合理的,反而应该把通过预读加载进来的缓存页刷入磁盘,因为它几乎没人访问。

哪些情况会触发预读机制呢?

(1)innoDB有一个参数,innodb_read_ahead_threshold,它的默认值是56,表示如果按顺序访问一个区里的多个数据页,访问数据页的数量超过了这个值,就会触发预读,把相邻区中的数据页都加载到内存区。

(2)Buffer Pool里缓存了一个区里的13个连续数据页,此时就会触发预读机制。这个机制是通过参数innodb_random_read_ahead来控制的,它默认是关闭的。

所以,一般情况下,只有第一种情况下,会触发预读机制,一下子把很多相邻的数据页加载到缓存去,这些缓存页如果一下子都放在LRU链表的前面,会把本来频繁访问的缓存页放到LRU链表尾部。需要淘汰时,就把这些高访问频率的缓存页淘汰掉。这是不合理的!

其实,最常见触发第一种情况的场景,就是全表扫描。

比如,执行SQL:SELECT * FROM USER。

查询表里的所有数据,会把磁盘里的数据页都加载到Buffer Pool里去,这时LRU链表头部就会有大量的连带加载进来的缓存页,这次SQL查询后,几乎不会再访问到。

如何优化预读?

为了解决上面我们说的预读问题,MySQL在设计LRU链表的时候,并不是简单的Least Recently Userd,而是采用了冷热数据分离的思想

MySQL的LRU链表分为两部分,一部分是热数据,一部分是冷数据,冷热数据的比例由参数innodb_old_blocks_pct控制,默认值37,也就是冷数据默认占37%。

图片

冷热数据区运作原理

当数据页第一次被加载到Buffer Pool的时候,会被放在冷数据区的头部,如图3所示。

图片

当对冷数据区进行频繁访问后,就会挪到热数据去,这个阈值是由参数innodb_old_blocks_time控制的,其默认值是1000毫秒。

意思就是,一个数据页被加载到缓存页后,在1秒之后,你访问了这个缓存页,它才会被挪动到热数据区的头部。

因为你加载一个数据页到缓存,过了1秒后还会访问这个页,说明你后续会经常访问它,那就放到热数据区吧。如果是1秒内访问,就会判断你以后不会经常访问这个缓存页,也就不会挪到热数据区。

图片

假设现在有一条SQL触发了全表扫描,会加载一大堆缓存页到Buffer Pool,冷热数据分离方案是怎么解决之前的问题的?

这时,预读加载的数据页会放在冷数据区前面。而热数据区不受影响,之前热数据区频繁访问的数据页,现在还可以继续访问。

现在过了1秒种后,如果这些预读加载进来的一大堆缓存页被访问了,那么就会挪动到热数据区头部去。

冷热数据分离思想下,如何淘汰数据?

假设现在缓存页不够了,需要淘汰一些缓存页,怎么办?

直接淘汰冷数据区尾部的缓存页!

因为它们只是被加载进Buffer Pool用了下,1s后就没再使用了,所以可以直接淘汰。

图片

如果你喜欢本文,

请长按二维码,关注 南山的架构笔记

图片

转发至朋友圈,是对我最大的支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值