BufferPool之链表

7 篇文章 0 订阅
1 篇文章 0 订阅

系列文章目录

MySQL之BufferPool



前言

提示:这里可以添加本文要记录的大概内容:

本文会介绍free链表,flush链表,LRU链表是什么,以及在Buffer pool中的作用与使用


一、free链表

1.1结构简图

free链表

1.2 结构说明

free链表组成: 由双向链表构成,free_pre指向上一个节点,free_next指向下一个节点,基节点中count代表空闲节点数量

free链表作用: free链表管理BufferPool中空闲缓冲页,BufferPool初始化后,所有缓冲页都是空闲,每个缓冲页对应的控制块都会加入free链表中

free链表基节点:管理free链表的节点,由free链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息。另外开辟的内存,不保存在BufferPool中

free链表使用: 从磁盘中加载一个页到BufferPool中时,检查hash表中是否已经加载该缓冲页(了解hash表结构),如果加载了,则直接读取,如果没有则从free链表中取出一个空闲缓冲页,并将该缓冲页对应的控制块信息填入,然后把该缓冲页对应的free链表节点(控制块)从free链表中移除,count-1

二、flush链表

2.1 结构简图

flush链表

2.2 结构说明

flush链表组成: 由双向链表构成,free_pre指向上一个节点,free_next指向下一个节点,基节点中count代表脏页节点数量

flush链表作用: 管理脏页,将需要刷新到磁盘的脏页保存在flush链表中

flush链表使用: 同样产生了脏页就加到flush链表中,等待刷新到(同步)磁盘中

三、LRU链表

3.1 结构简图

LRU链表

LRU链表组成: 全称Least Recently Used链表。由双向链表构成,free_pre指向上一个节点,free_next指向下一个节点,基节点中count代表节点数量

LRU链表作用: 解决BufferPool内存有限问题,淘汰使用的比较少的缓冲页。按照最少使用原则进行淘汰

LRU链表使用: 如果该页不在BufferPool中,将该页从磁盘加载到BufferPool的缓冲页时,将free链表节点减去,把该缓冲页对应得控制块作为节点塞到LRU链表得头部,如果以及加载在BufferPool中,则将该页对应得控制块移动到LRU链表得头部

LRU链表的局限性:预读问题, 针对全表扫描时占用大量使用频率非常低的页面问题。这两种问题都严重降低了BufferPool的命中率

3.2 LRU优化后结构简图

LRU链表优化后结构图

LRU链表的优化点:如图,将LRU链表分为young区域和old区域。其中young区域存储热点数据,old区域存储使用频率低的数据。
可通过系统变了innodb_old_blocks_pct控制,old区域默认占比37%

LRU链表的优化内容

  • 针对 预读问题:初次加载到BufferPool的缓冲页对应的控制块会放到old区域的头部,这样既不会影响到young中的热点数据,如果预读后不进行访问则会逐渐被淘汰
  • 针对 针对全表扫描时占用大量使用频率非常低的页面问题:我们知道全表扫描时会对每一条数据进行访问,而数据都存在页中,所以全表扫描时会对页进行多次访问,所以针对预读问题能解决的办法并不适用于全表扫描问题,故此提供了一个时间间隔来处理该问题。
    对全表扫描加入old区域的缓冲页,进行第一次访问时记录这个时间点,如果后续访问时间与第一次访问的时间点在某个时间点内(系统变量innodb_old_blocks_time控制这个时间点大小,默认值为1000ms),那么该页面就不会从old区域移动到young区域,否则会将其移动到young区域的头部

其它优化方式: 目的提高BufferPool命中率

  • 优化问题:young区域缓冲页每次访问一个缓冲页就要将该缓冲页移动到LRU链表头部,移动频率较高

为了管理好BufferPool,还有很多链表结构,unzip LRU链表,zip clean链表等等

附录

脏页: 与磁盘上的页数据不一致的缓冲页(其实也就是内存与磁盘数据不一致),这类页需要与磁盘同步

redo日志:记录数据库修改删除操作,满足系统崩溃后数据还能保证恢复的功能(持久性)。

lsn值: lsn值代表系统写入的redo日志量的一个总和

checkpoint: 因为redo日志文件组容量有限,所以redo日志是循环使用的,而checkpoint就是覆盖的redo日志操作

预读问题:预读出的数据,用不到的话就会白白占用BufferPool内存,同时还可能挤压到其他语句在BufferPool中的内存空间
InnoDB提供了预读功能:如果InnoDB认为执行当前请求时,可能会在后面读取某些页面,就会预先将这些页面加载到BufferPool中。根据触发方式分为:

  • 线性预读:由系统变量innodb_read_ahead_threshold控制,默认值是56。如果顺序访问某个的页面超过这个系统变量,就会触发一次异步读取下一个区中全部的页面到BufferPool中的请求。
  • 随机预读:由系统变量innodb_random_read_ahead控制,默认值为OFF(关闭)。如果某个区的13个连续的页面都被加载到BufferPool中,无论这个页面是不是顺序读取,都会触发一次异步读取本区中所有其他页面到BufferPool中的请求。

针对全表扫描时,占用大量使用频率非常低的页面问题: 全表扫描读取大量页,而BufferPool又没有足够空间容纳的话,就会将其他语句占用的BufferPool空间给覆盖,这样其他语句在执行的时候又需要重新从磁盘中加载页到BufferPool中。
使用频率非常低的页占用了使用频率非常高的页在BufferPool的内存,严重降低了BufferPool的命中率。

:表空间的页太多了,为了更好的管理这些页,InndDB提出来区的概念,对于16kb的页来说,连续的64个页就是一个区,也就是一个区默认占用1M空间,每256个区划分为一组

其它:一般情况下都是后台线程对flush链表与LRU链表刷脏页,但是如果当前系统页面修改操作十分频繁,这就导致redo日志的操作十分频繁,系统lsn值增长过快,后台线程不能将脏页快速刷出,无法及时执行checkpoint,可能就会用到用户线程刷脏页(只有刷完脏页了,redo日志才会失效,才可以执行checkpoint),当然用用户线程刷脏页,肯定会影响到mysql效率,读写分离,能提高效率也有解决这一层问题的原因


想要体系的了解BufferPool,建议看书。
作者写的很好,通俗易通,读之前建议读者先了解基本的数据结构,有过mysql使用经验的读起来会更有趣

参考资料: 《MySQL是怎样运行的》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值