【10. MySQL 的Buffer Pool】

1. 概念

  • MySQL 的数据是存储在磁盘里的,不能每次都从磁盘里面读取数据.这样会使得性能非常差。

  • 于是提升查询性能的话,需要加一个缓存,因此Innodb 存储引擎设计了一个缓冲池(Buffer Pool)

  • 有了缓存池后:

    • 当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。
    • 当修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。
  • 默认配置下 Buffer Pool 只有 128MB

2. Buffer Pool缓存的内容

  • InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool 同样需要按「页」来划分。
  • 在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。
  • Buffer Pool 除了缓存「索引页」「数据页」,还包括了 undo 页,插入缓存、自适应哈希索引、锁信息等等。
    在这里插入图片描述

控制块

  • 为了管理Buffer Pool中的缓存页,InnoDB 为每一个缓存页都创建了一个控制块
    • 控制块信息包括「缓存页的表空间、页号、缓存页地址、链表节点」等等
  • 每一个控制块都对应一个缓存页
    在这里插入图片描述

问题1:查询一条记录,就只需要缓冲一条记录吗?

  • 当我们查询一条记录时,InnoDB 是会把整个页的数据加载到 Buffer Pool 中,因为,通过索引只能定位到磁盘中的页,不能定位到页中的记录。当把页加载到Buffer Pool后,在通过页里的页目录定位到某条具体的记录

3. Buffer Pool的管理

Innodb 通过三种链表来管理缓页:

  • Free List (空闲页链表),管理空闲页;
  • Flush List(脏页链表),管理脏页(逻辑同空闲链表);
  • LRU List,管理脏页+干净页,将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。;

Free List解释:

  • Buffer Pool 是一片连续的内存空间,当 MySQL 运行一段时间后,这片连续的内存空间中的缓存页既有空闲的,也有被使用的。
  • 当我们从磁盘读取数据的时候,不能通过遍历这一块连续的内存空间找到空闲的空间,这样效率太低。
  • 为了更快找到空闲缓存页,可以使用链表结构,将缓存页的控制块作为链表的节点,这个链表成为Free链表

链表信息:

  • Free 链表上除了有控制块,还有一个头节点,该头节点包含链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息。
  • 每一个链表节点对应一个进行控制块,也就相当于对应一个空间缓存页。
  • 当每次从磁盘加载一个页到Buffer Pool中时,就从Free链表中的取出一个空闲缓存页,并且把该缓存页对应的控制块的信息填上,然后把该缓存页对应的控制块从 Free 链表中移除。
    在这里插入图片描述

Flush List解释

  • 和空闲链表一样,为了提高Buffer Pool的读性能,也就是更新数据时,不需要每次都要写入磁盘,而是将 Buffer Pool 对应的缓存页标记为脏页,然后再由后台线程将脏页写入到磁盘。

提高缓存命中率

  • InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,当Buffer Pool中满了,就会淘汰尾部的节点。但是可能会导致预读失效和Buffer Pool 污染于是 InnoDB 做 2 点优化来解决上述两个问题:
    • 将 LRU 链表 分为young 和 old 两个区域,加入缓冲池的页,优先插入 old 区域;页被访问时,才进入 young 区域,目的是为了解决预读失效的问题。
    • 当**「页被访问」且「 old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)」**时,才会将页插入到 young 区域,否则还是插入到 old 区域,目的是为了解决批量数据访问,大量热数据淘汰的问题
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

脏页什么时候会被刷入磁盘?

  • 当修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,但是磁盘中还是原数据。因此,脏页需要被刷入磁盘,保证缓存和磁盘数据一致,一般都会在一定时机进行批量刷盘(该操作是由操作系统做的)。
  • InnoDB 的更新操作采用的是 Write Ahead Log 策略,即先写日志,再写入磁盘,通过 redo log 日志让 MySQL 拥有了崩溃恢复能力。解决脏页还没有来得及刷入到磁盘时,MySQL 宕机了,导致数据丢失问题
  • 下面几种情况会触发脏页的刷新:
    • 当 redo log 日志满了的情况下,会主动触发脏页刷新到磁盘;
    • Buffer Pool 空间不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
    • MySQL 认为空闲时,后台线程会定期将适量的脏页刷入到磁盘;
    • MySQL 正常关闭之前,会把所有的脏页刷入到磁盘;

问题:

  • 在我们开启了慢 SQL 监控后,如果你发现**「偶尔」会出现一些用时稍长的 SQL**,这可能是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

如果间断出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

4. 面试

MySQL的buffer pool和操作系统里的page cache有什么区别 为什么MySQL 不直接用page cache

  1. buffer pool 是innodb存储引擎自己实现缓冲区,在应用层,主要是缓冲数据页,默认页的单位是16K,page cache内核实现缓冲区,主要是缓冲页(内存页,文件页),通常一页的单位是4K。
  2. 如果mysql直接使用page cache就没办法自己定制缓存淘汰算法,也不好管理数据页,比如没办法知道哪些数据页是脏的。

文章来源:https://xiaolincoding.com

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小呆鸟_coding

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

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

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

打赏作者

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

抵扣说明:

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

余额充值