细说InnoDB缓冲池 buffer pool(free、flush、lru)

视频地址 https://www.bilibili.com/video/BV1C3411t7WL

一、开篇

在InnoDB引擎中对数据库增删改查,都是先从磁盘中把数据加载到内存,然后在内存中进行相关操作,我们把这块的内存称之为 buffer pool (缓冲池)

既然这是内存中的一块区域,那么它就一定有大小(默认是128M),如果你有一个16G的数据库服务器,你安装好MySQL,那你的缓冲池的大小就是128M,这时候肯定很影响你的操作,因为它太小了,所以我们应该根据当前条件来对这个参数进行修改:innodb_buffer_pool_size

说到数据库,大家脑海里肯定是库、表、行、字段,这其实没错毕竟我们对它就是这样操作的,这其实也错,因为我们知道数据是存在硬盘中的,硬盘怎么会有这些个逻辑数据呢?肯定是都存储的物理数据。

在硬盘中,MySQL把16KB作为一个单位,我们把这个叫做数据页,一页里面可以存储很多的数据,页之间是有双向指针进行关联的。

从硬盘中读取数据到缓冲池也是以页为单位的,在缓冲池中有一个叫做缓存页的东西,它也是16KB大小,每读取一个数据页就把它放在一个空闲的缓存页里面。

除了这个缓存页之外还有一个叫做元数据的东西,你可以这样理解这个每个元数据和每个缓存页一一对应,它相当于是缓存页的头,它里面存储了缓存页的基础信息,下面你就会明白它的作用了。

好了,到这里我们就知道了buffer pool里面有缓存页(16KB),元数据,那么你的脑海里应该浮现这样的图

在这里插入图片描述

buffer pool默认的大小128M,是用来存储数据的,也就是缓存页,但同时也还要存储元数据,所以实际上它的大小会比128M要大一些。


二、free链表

上面说到,我们是把硬盘中的数据页一片片加载到buffer pool的缓存页里面去,那这里就存在一个问题:怎么知道哪些数据页是空的呢?我们不可能去覆盖别的数据对不对?

其实这里维护了一个free链表,它是由空闲的元数据组成的(前面我们说了这个元数据和缓存页是一一对应的,所以一个空闲元数据就对应一个空缓存页),这样我们就很容易的知道那些缓存页是空的、还有多少空的缓存页。

在这里插入图片描述

这个基本结点是单独的空间,它里面就存储了链表长度等其它的数据。

如果有新的数据页要加载到缓存中,就从free链表里面取出一个元数据找到对应的缓存页,然后放进去,在把这个元数据从free链表里面剔除。

可以理解成你在Java中的链表,伪代码实现 head.next = head.next.next


三、flush 链表

上面的free链表是标示空闲的缓存页,而我们的修改数据也是在缓存页里面操作的,这些数据并未存储到磁盘中去,毫无疑问肯定是要记录哪些缓存页是已经修改了的,不然系统怎么把它们刷进磁盘呢?而这个记录的地方也就是 flush链表

flush链表也是和上面free链表一样:基于元数据组成的。当有一个缓存页被修改之后就把它加入到flush链表里面去。

注:可能有人有疑问为啥这个元数据可以组成free链表又能组成flush链表呢?其实熟悉链表结构的朋友都知道,链表只是一个指向的问题而已,我们可以在元数据里面定义N个,next、front,指针去指向就好了。

因为flush链表和free链表结构基本上是一样的,所以就不花了,但是要理解不是单独的两个链表,而是两种指向。

有了flush链表,当要把数据刷回磁盘的时候就容易多了,具体何时刷入下面再说。


四、LRU 链表

这里我们还有一个问题,我们的数据都是存储在缓存中的,而缓存大小是有限的,有些数据很可能就是查询一次就不再使用了,如果让它一直存储在缓存中那肯定是不合适的。所以这里我们引入一个新的链表 LRU链表 (Least Recently Used)最近最少使用的意思,也就是我们可以使用这个链表来维护热点和非热点数据。

注:这个链表的节点也是基于元数据来组成的,但是和上述两个链表还是有些差异。

何为热点数据,这其实是一个定义问题。当然按照顺序排序把最近访问的数据放到链表头部,那这样的话前面的数据就是热点数据。

但是MySQL有两个操作会让上面这种方式产生漏洞,全表扫描预读


全表扫描

这很好理解,比如你写一个不带任何条件的查询SQL: SELECT * FTOM t_user,这会加载很多的数据页到缓存中,但实际上很多数据只用一次就不再使用了。


预读

在InnoDB引擎下,当你一次读取超过56页数据的时候就会触发预读,也就是在InnoDB看来,如果你一下次读取了56页数据,那么你大概率也是要读取57、58…

这个56是来自默认的配置,当然你页可以修改它的配置文件 innodb_read_ahead_threshold


冷热区域

为了解决上面的这个“漏洞”,真实的LRU链表是分冷热区域的,当数据页从磁盘加载到内存的时候,会把它放在冷区域的头部,当超过1s后再次去访问这个数据的时候才会把它移动到热区域

  • innodb_old_blocks_pct 冷区域的大小默认是 37%
  • innodb_old_blocks_time 多少毫秒之后访问冷数据,会加载到热区域 默认 1000

按照我们的理解如果超过1s后再访问冷区域的数据就把它移动到热区域,这个是没问题的。

但如果我们访问热区域的数据,是不是每次访问就把它移动到热区域的头部呢?这样看似合理,实则不合理,你想想频繁的移动也是需要消耗性能的,所以只有在访问热区域的后75%的数据才会移动到热区域的头部

在这里插入图片描述


五、数据回盘

buffer pool是内存的一块区域,它总会有满的时候,如果它满了那怎么办呢?其实通过上面的几个链表我们已经知道了,如果满了,可以把LRU链表的冷区域的数据进行刷回磁盘操作就好了。

另外一个问题就是flush链表里面的脏页何时被刷回磁盘呢?它有两个操作,一个是上面的磁盘空间不足的时候会把LRU链表里面的冷数据刷回磁盘,这里面也包含了一些flush链表里面的数据,一个是后台有一个IO线程会不定期的把flush链表里面的脏页刷回磁盘。

注:free、flush、lru 都是指的相同的数据,只不过是按照不同的指向组成了不同的链表而已。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值