深入理解InnoDB的Buffer Pool

1.缓存引入的重要性

InnoDB是通过页来管理数据的,页是存放在表空间中,表空间说白了就是对数据文件的抽象,数据始终是存储在磁盘上的,如果每次CRUD的操作都要和磁盘进行IO交互,那MySQL的性能将会大打折扣,吞吐量页没法保证。此时InnoDB就需要引入一个Buffer缓冲区来优化这个问题,当客户端要访问某个页面或者数据时,可以直接从Buffer缓冲区中快速读取,这个缓冲区是在内存中的,速度不言而喻。而且这个Buffer会将页数据缓存一段时间,以便于再次访问页数据时能够快速定位,提高效率。
总之,缓存的引入就是为了减少磁盘的IO操作。但是缓存的引入也带来了许多问题,例如:内存资源问题、Buffer管理维护问题、数据一致性问题等。

2.InnoDB的Buffer Pool是什么

从名字上看就能明白这是一个基于内存的一个缓冲池子,这个pool池中缓存了一个一个的页。MySQL服务器启动的时候就会向操作系统申请一块连续的内存空间,这个连续内存空间叫Buffer Pool
这个Buffer Pool可以通过系统参数调节innodb_buffer_pool_size,默认只有128MB,最小5MB。

3.Buffer Pool的组成结构和原理

3.1 Buffer Pool的基本组成

在这里插入图片描述

碎片:每一个缓冲页都有对应的控制块,这些控制块和缓冲页的大小和Buffer Pool大小如果不是很对应倍数,那么就存在碎片区域。每个控制块的大小占用很小,基本不到页大小的5%,连1KB都不到。

3.2 free空闲链表管理

MySQL服务启动的时候,向操作系统申请了一块连续的内存空间Buffer Pool,但是这个缓冲池里没有任何数据页面和控制数据,此时控制块和缓冲页里的内容是空的,当有读取操作任务时,从磁盘读取页数据到Buffer Pool中,但是具体将页放在那儿呢?这时候就需要一个和JVM分配内存类似的结构空闲链表来维护和提供空闲地址的信息。
在这里插入图片描述

其中,这个链表中的控制块就是Buffer Pool中的控制块,只不过被free基几点组成了一个双向链表。其中这个基结点/头结点不属于Buffer Pool缓冲池内,它占用的空间很小,只有40字节

3.3 缓冲页的哈希表快速定位

当我们要访问Buffer Pool查询某个页面是否在缓冲池中时,不可能遍历整个缓冲页。InnoDB基于哈希表的思想,将表空间号 + 页号作为key,控制块地址就是value。通过哈希表的O1时间复杂度,就可以快速定位某个页是否在其中了,找到了控制块,那么缓冲页同样也就找到了。

3.4 Flush脏页链表管理

脏页:如果SQL语句修改了某个页面的数据,此时Buffer Pool中的缓冲页数据就和磁盘上的数据不一致了,这样的页称之为脏页。
脏页毫无疑问肯定是要刷新回磁盘的,但是如果每修改一次就刷新一次,这未免太频繁了,对性能有很大影响。所以InnoDB设计了将脏页组成一个链表来管理何时刷盘等操作。
在这里插入图片描述

很显然,一个控制块不可能即是空闲链表的节点又是脏页链表的节点。

3.5 LRU缓冲管理链表

3.5.1 Buffer Pool空间不够了怎么办?

Buffer Pool毕竟是内存资源,也有用完的时候。作为MySQL提高性能的利器,如果出现了缓冲池大小不够用的情况,也不可能无限的去操作系统申请内存;肯定是有自己的一套管理内存淘汰相关的策略。当发现Buffer Pool不够用的时候,肯定是要淘汰掉一些不常用的页面。

3.5.2 使用普通的LRU链表管理淘汰策略有什么问题?

LRU又派上了用场,如果使用LRU链表的形式来管理。当访问一个页面时将该页面控制块挪到链表头部,后续如果发生了内存不够用的情况,就淘汰掉链表末端的节点即可。
但是这种简单的LRU方式有两个大问题:

        - InnoDB有预读的功能,结合空间局部性原理,一次性从磁盘读取多个页面到Buffer Pool中,如果许多页面压根用不上怎么办?
        - 如果有全表扫描这样的场景,整颗B+Tree的页面都放到Buffer Pool中,将其它缓冲页挤出去,影响到了其它热点的缓冲页。

3.5.3 预读

        - `**线性预读**`**:如果连续访问某个区的页面超过了阈值(默认56,可调),那么innoDB就会触发一次异步任务,将下一个区中的全部页面加载到Buffer Pool中。**
        - `**随机预读**`**:如果某个区中连续13个页面都被加载到了Buffer Pool中,那么innoDB就会触发一次异步任务,将当前区中的剩下页面都加载到Buffer Pool中。随机预读功能默认是关闭的,也可以通过参数开启。(这13个页面必须是热页面,要在LRU的yong区前1/4范围)**

3.5.4 分区域的LRU链表管理淘汰策略

热点转移的思想就可以用在这里,innoDB将LRU链表进行了分区域的管理,按照一定的比例分成两截:

        - `热数据区、young区`:存储使用频率很高的缓冲页的控制块。
        - `冷数据区、old区`:存储使用率不是那么高的缓冲页的控制块。

在这里插入图片描述

3.5.5 分区域的LRU优点

        - 当有新的缓冲页加载到Buffer Pool来,会被放到old区域的头结点上。
        - old区的控制块被读取的时候不会立即放入yong区的头,而是会判断上一次访问该控制块的时间间隔,如果大于某个间隔(默认1s,可调)就不会被放入yong区。
        - 只有访问yong区的1/4之后的区域的缓冲时,才会挪动这些控制块到yong的头结点,因为没必要每次访问yong区的缓冲都要挪动,毕竟yong区本来就是热点缓冲,没太大必要操作这么频繁。

4.脏页的刷盘

MySQL服务后台线程有专门的的线程定时周期性的将脏页刷新到磁盘,有两种方式:

4.1 从LRU遍历寻找脏页刷盘

后台线程会定时从LRU链表的尾部开始扫描控制块,找到有脏页的控制块,然后刷盘这个缓冲页面。

4.2 从Flush脏页链表取一部分刷盘

后台线程定时从flush脏页链表中刷新一部分的缓冲页面,刷盘的速率等取决于MySQL服务的繁忙程度。

5.多Buffer Pool实例

Buffer Pool本质上是一段连续的内存空间,在多线程访问的情况下对Buffer Pool的各个链表操作都要加锁处理。那么整个MySQL只有一份儿缓冲池的设计肯定是不行的,MySQL就提供了一个参数:innodb_buffer_pool_instances来控制申请Buffer Pool实例的个数。每个Buffer Pool的链表相互独立、内存也独立。这样多线程访问的时候通过热点分散思想就提高了整个MySQL服务效率。
在这里插入图片描述

5.1 chunk

早期Buffer Pool的实例、大小等参数在MySQL启动时就要指定,不能在运行时修改。在MySQL5.7.5版本后,推出了chunk的概念,一个Buffer Pool中包含多个chunk,每一个chunk就是一个连续的物理空间,需要向操作系统申请。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Minor王智

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值