Buffer Pool 核心原理

MySQL的BufferPool是数据库性能优化的关键,它缓存数据页以减少磁盘I/O。BufferPool包括数据页、缓存页描述信息、free链表、flush链表、LRU链表等组件。数据页是数据库存储的基本单位,BufferPool通过哈希表快速查找数据,使用LRU策略管理内存,后台线程定期刷新脏数据。预读机制和冷热数据分离确保高效使用缓存。
摘要由CSDN通过智能技术生成

Buffer Pool整体介绍:

数据库最终的操作都会刷入磁盘,但是如果直接对磁盘进行操作,那么速度将会很慢,每秒只能处理几百个请求。所以实际上对数据库进行操作的时候,都是针对Buffer Pool中的数据进行的,同时通过undo log、redo log、binlog来保证数据的正确性,以及不丢失数据。

Buffer Pool是Mysql架构中的一个核心组件。

Buffer Pool其实就是一片内存数据结构,默认大小为128M,通过设置innodb_buffer_pool_size参数可以进行调整。

数据页:

数据库的核心数据模型是:表+字段+行,但是数据并不是以这样的逻辑概念去存储的。Mysql对数据抽象出来了一个数据页的概念,把很多行数据放在了一个数据页里面。磁盘文件中包含了很多数据页,每一个数据页放了很多行数据。

一个数据页默认大小为16KB。

如果要去更新一行,此时数据库会找到这行数据所在的数据页,然后把该数据页直接加载到Buffer Pool里去。

缓存页:

加载到Buffer Pool中的数据页成为缓存页,缓存页与数据页一一对应,都是16KB。

缓存页描述信息:

 每一个缓存页都有一个描述信息,用来描述这个缓存页,比如包含如下一些东西:数据页所属表空间,数据页的编号,数据页在Buffer Pool中的地址等等。

缓存页描述信息也在Buffer Pool中,在Buffer Pool中每个缓存页描述信息放在最前面,各个缓存页放在最后面。

描述信息大概占缓存页大小的5%左右。假设你的Buffer Pool大小为128M,实际上Buffer Pool真正最终大小会超过一些,可能130M的样子。

思考:为什么要引入缓存页的概念?

Buffer Pool通过缓存页,很大程度上避免了内存碎片的产生,只要让缓存页紧密排列即可。

思考:为什么要引入数据页的概念?

如果没有数据页,那么每次更新或读取的时候,只把磁盘里的一条数据加载到内存里去,然后下次再需要更新别的数据的时候,再从磁盘里加载另一条数据到内存中去,这样每次都是一条数据,会有大量的磁盘IO,效率很低。而使用数据页可以避免大量不必要的重复的读取过程,降低磁盘IO次数,提高效率。

Free链表:

数据库启动的时候,会初始化Buffer Pool。按照Buffer Pool大小,稍微再大一点,去找操作系统申请一块内存区域,然后按照默认的缓存页的默认16KB的大小以及对应的800B的缓存页描述信息的大小,再Buffer Pool中划分出一个一个的缓存页及其对应的描述信息。

这个时候,缓存页都是空的。只有在具体进行增删改查操作的时候,才会把对应的数据从磁盘上取出来放入到Buffer Pool的缓存页中。

那如何判定哪些缓存页是空的呢?Buffer Pool中有一个free链表,是一个双向循环链表,其中每一个节点就是一个空闲的缓存页的描述信息的地址。这个free链表还有一个基础节点,它会引用链表的头节点和尾节点,存储了空闲缓存页数量信息。

 Free链表本身也是Buffer Pool的一部分,其每个节点都是存储了缓存页描述信息的地址的一个指针。其中基础节点不属于Buffer Pool。

如何将磁盘上的数据读取到Buffer Pool中?

首先从free链表中获取一个描述信息块,然后可以找到对应的缓存页描述数据,然后可以找到空闲的缓存页;

然后把磁盘上的数据加载到对应的缓存页中去,并维护缓存页的描述数据;

最后把对应的描述数据指针从free链表上删除。

哈希表数据结构:

如何知道一个数据是否被缓存?

我们在执行增删改查的时候,肯定是先查看这个数据是否被缓存了,如果没有被缓存就走上面的逻辑,但是如果被缓存了话,就直接使用缓存中的数据。

哈希表数据结构,用表空间号+数据页号作为key,缓存页地址作为value。当使用一个数据的时候,先查哈希表,如果查到了说明Buffer Pool中已经有了该缓存页,如果没有说明Buffer Pool中没有该缓存页。

Flush链表:

在执行更新操作的时候,实际上是更新了Buffer Pool对应的缓存页中的数据,这个时候需要把缓存页中的数据刷入磁盘,那么如何判断该哪些缓存页做了修改(有了脏数据),该把哪些数据刷入磁盘呢?这就用到了flush链表。

Flush链表和free链表一样,也是一个双向循环链表,每个节点记录的是指向缓存页描述信息的指针,只不过free链表针对的是空闲缓存页,而flush链表针对的是被修改过的缓存页。

凡是被修改过的缓存页,都是把它的描述信息加入到flush链表中。

LRU链表:

Buffer Pool在内存中,容量是有限的,远远小于数据库可以存储的数据量,那么如果Buffer Pool中的缓存页不够了怎么办?

所以需要有内存淘汰机制,Mysql采用的是LRU,最近最少使用内存淘汰机制,去淘汰掉最近最少使用的缓存页。

所谓淘汰缓存页,就是如果这个缓存页被修改过的话,先把缓存页刷入磁盘,然后清空这个缓存页,让它重新变成一个空闲缓存页。

引入LRU链表来判断哪些缓存是不常用的,然后淘汰掉不常用的缓存页。

LRU链表工作原理:

从磁盘加载一个数据页到缓存页的时候,就把这个缓存页的描述数据放入到LRU链表头部,只要被使用到就在LRU链表里有记录,而且越是最近被使用到的就越靠近头部,而最近最少使用的缓存页就一定越靠近尾部。

当没有空闲的缓存页的时候,找到最近最少使用的那个缓存页去清空,那么直接找LRU链表尾部的缓存页即可。

冷热分离的LRU链表:

因为Mysql预读机制的存在,以及全表扫描的情况,那么就会把大量的可能并不经常使用的数据读取了Buffer Pool的缓存页中去,导致原本访问频繁的缓存页走到了LRU链表的尾部被清空。

 设立预读机制的原因:

假设你读取了数据页01到缓存页中,接下来有可能顺序读取数据页02到缓存页中,这个时候是不是可能在读取数据页02的时候需要再次发起一次磁盘IO?所以为了优化性能,MYSQL才设计了预读机制:如果在一个区内,你顺序读取了数据页01到数据页56,MYSQL会判断你后面会继续顺序读取后面的数据页,此时提前把后面的一大堆数据页都读取到Buffer Pool中。

Mysql真正的LRU链表实际上分为两个部分:一部分是冷数据,一部分是热数据,冷热比例由innodb_old_blocks_pct参数控制,默认为37,也就是说冷数据占比为37%

1、数据第一次被加载到缓存的时候,会放在冷数据的链表头部,

2、一个数据页被加载到冷数据区域后,为了除去第一次的访问(加载之后随即的那次访问),设置参数innodb_old_blocks_time,默认为1000,1000毫秒。也就是说数据加载到缓存页之后,在1s之后,你访问这个缓存页,它才会被挪动到热数据区域的链表头部去。

这样通过预读机制、全表扫描进行的数据,全部都在冷数据区域,不会对频繁访问的热数据造成影响。而且需要1s之后有访问才会把冷数据区域的缓存页挪动到热数据区域。

如果缓存不够用了的话,淘汰冷数据区域的尾部数据。

优化LRU链表的热数据区域:

热数据区域的数据访问频繁,如果每次只要访问到,就把对应的缓存页描述数据移动到LRU链表头部,这样频繁移动性能是不是不太好?而且也没有这个必要。

优化:只有热数据区后3/4的缓存被访问到了的话,才会移动到LRU链表头部;而前1/4的缓存页被访问到是不会移动的。

总结:

Buffer Pool在被使用到的时候,实际上会频繁的从磁盘中加载数据页到缓存页中去,然后伴随着free链表,flush链表,lru链表的更新。

1、当数据加载到一个缓存页的时候:free链表中移除这个缓存页,lru链表的冷数据区域头部会放入这个数据页。

2、要是修改了一个数据页:flush链表会记录这个脏数据页,lru链表可能会把这个数据页从冷数据区域移动的热数据区域头部。

3、如果是查询一个缓存数据页:此时可能会把这个缓存页在lru链表上从冷数据区域或是热数据区域移动的热数据区域头部去。

4、如果一个缓存页被刷入磁盘:这个缓存页会被加入到free链表中,从flush链表中移出,从lru链表中移出。

总之,MYSQL在执行CRUD的时候,其实就是在操作大量的缓存数据页以及对应的几个链表。

后台IO线程将LRU尾部缓存页刷入磁盘:

后台线程具体什么时候把LRU链表的冷数据区域中的缓存刷入磁盘的呢:

  1. 定时把LRU尾部的部分缓存页刷入磁盘:所以实际上在缓存页还没有用完的时候,就会清理一些缓存页了,然后清空的缓存页又可以用于放新的数据。
  2. 把flush链表中的一些缓存页定时刷入磁盘:由于flush链表中的缓存页里面存有脏数据,需要刷入磁盘,所以这个后台线程在不怎么繁忙的时候,会把flush链表中的缓存页都刷入磁盘中,这样被你修改过的数据,迟早都会刷入磁盘的。
  3. 实在没有空闲缓存页的时候:同样会将LRU链表冷数据区域尾部的缓存页刷入磁盘,腾出新的空闲缓存页用于存放新的数据。

Mysql内部架构_jerry_dyy的博客-CSDN博客

Mysql存储模型_jerry_dyy的博客-CSDN博客

InnoDB内部架构_jerry_dyy的博客-CSDN博客

Buffer Pool 核心原理_jerry_dyy的博客-CSDN博客

Buffer Pool生产实践_jerry_dyy的博客-CSDN博客

Mysql事务隔离机制_jerry_dyy的博客-CSDN博客

Mysql的锁机制_jerry_dyy的博客-CSDN博客

Mysql的索引深度讲解_jerry_dyy的博客-CSDN博客

Mysql索引的使用_jerry_dyy的博客-CSDN博客

SQL语句的执行计划_jerry_dyy的博客-CSDN博客_sql语句的执行计划

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值