【MySQL03】【 Buffer Pool】

文章目录

  • 一、前言
  • 二、缓冲池(Buffer Pool )
    • 1. 缓冲池的概念
    • 2. LRU List、Free List 和 Flush List
      • 2.1 Free 链表
        • 2.1.1 缓冲页的哈希处理
      • 2.2 Flush 链表
      • 2.3 LRU 链表
        • 2.3.1 简单 LRU 链表
        • 2.3.2 优化后的 LRU 列表
        • 2.3.3 更进一步的优化
    • 3. 脏页的刷新
    • 4. 多个 Buffer Pool 实例
  • 三、重做日志缓冲(redo log buffer)
  • 四、参考内容

一、前言

最近在读《MySQL 是怎样运行的》、《MySQL技术内幕 InnoDB存储引擎 》,后续会随机将书中部分内容记录下来作为学习笔记,部分内容经过个人删改,因此可能存在错误,如想详细了解相关内容强烈推荐阅读相关书籍。


二、缓冲池(Buffer Pool )

1. 缓冲池的概念

InnoDB 存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可将其视为基于磁盘的数据库系统(Disk-base Database)。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。

缓冲池(Buffer Pool )简单来说就是一块连续的内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中这个过程称为将页“FIX”在缓冲池中。下一次再读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。

对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为 CheckPoint 的机制刷新回磁盘。

默认情况下 缓冲池(Buffer Pool )大小只有128M,可以通过 innodb_buffer_pool_size 参数设置。缓冲池(Buffer Pool )中的页类型有:索引页、数据页、undo页、插入缓冲、自适应哈希、InnoDB 存储的锁信息、数据字典信息等,不能简单的认为缓存池只是缓存索引页和数据页,他们只是占了很大一部分而已。1.0.x 开始允许有多个缓冲池实例,每个页根据哈希值平均分配到不同的缓冲池实例,减少了DB的内部资源竞争。如下图:

在这里插入图片描述

InnoDB中为每一个缓冲页都创建了一些控制信息,包括该页所属的表空间属性、页号、缓冲页在 Buffer Pool 中的地址、链表信息等。每个缓冲页对应的控制信息占用的内存大小是相同的,我们把每个页对应的控制信息占用的一块内存称为一个控制块控制块和缓冲页一一对,都存放在 Buffer Pool 中。如下图:

在这里插入图片描述

这里需要注意:

  1. 在 DEBUG 模式下,一个控制块占用缓冲页大约 5% 的大小,正常模式下会更小一些。
  2. 每个控制块都对应一个缓冲页,当剩余空间不足以分配一对控制块和缓冲页的大小时就会产生碎片,如上图。

2. LRU List、Free List 和 Flush List

2.1 Free 链表

在 MySQL 刚启动的时候,需要完成对 Buffer Pool 的初始化过程:首先向操作系统申请 Buffer Pool 的内存空间,然后将其划分为若干对控制块和缓冲页。但此时还没有真正的磁盘页被缓存到 Buffer Pool 中,随着程序的运行会不断有磁盘页被缓存到 Buffer Pool 中。那么就需要记录 Buffer Pool 中的那些缓冲页是可用的:将所有空闲的缓冲页对应的控制块作为一个节点放到一个链表中,称为 Free 链表(空闲链表)。刚完成初始化的 Buffer Pool 中,所有的缓冲页是空闲的。如下:
在这里插入图片描述

从上图可以看到:

  1. Free 链表存在一个基节点,包含链表的头节点、尾节点指针以及链表中节点数量等信息。需要注意这个基节点占用的内存并包含在 Buffer Pool 申请的一大片连续内存空间之内,而是一块单独申请的内存空间。
  2. 每当需要从磁盘加载一个页到 Buffer Pool 中时,就哦那个 Free 链表中取一个空闲缓冲页,并且把该缓冲页对应的控制块的信息填上(即该页所在的表空间、页号等信息),然后把该缓冲页对应的 Free 链接节点(也就是对应的控制块)从链表中移除,表示该缓冲页已经被使用。
2.1.1 缓冲页的哈希处理

当我们需要访问某页中的数据时,就会把该页从磁盘加载到 Buffer Pool 中,如果该页已经在 Buffer Pool 中则直接使用。那么如何定位一个页是否在 Buffer Pool 中?

InnoDB 实际上是通过表空间号 + 页好来定位一个页的,所以可以通过表空间号 + 页号 作为 key,用缓冲页控制块的地址作为 value 来创建一个 哈希表。在需要访问某个页的数据时,先从哈希表中根据表空间号 + 页号 看看是否有对应缓冲页,如果有则直接使用,否则从Free 链表中选择一个空闲的缓冲页,然后把磁盘中对应的页加载到该缓冲页的位置。

2.2 Flush 链表

如果我们修改了 Buffer Pool 中某个缓冲页的数据,此时会造成缓冲页与磁盘页的数据不一致,称为"脏页"(不能修改完一个页的数据就立即刷新到磁盘,不然会严重影响程序性能。所以每次修改完缓冲页后并不是立即刷新而是等到某个刷新时间点后才刷新)。那么InnoDB 就需要记录 Buffer Pool 中哪些页是脏页,以便后续将脏页刷新到磁盘中,因此就出现了一个 FLUSH 链表,凡是被修改过的缓冲页对应的控制块都会作为一个节点加入到这个链表。其结构与 Free 链表相同,这里不再赘述。

注意:某个缓冲页对应的控制块不可能即是 Free 链表的节点又是 Flush 链表的节点。因为如果一个缓冲页时空闲的就不可能是脏页,是脏页就不可能是空闲页。

2.3 LRU 链表

当 Buffer Pool 中不再有空闲的缓冲页时就需要淘汰一部分页。理想情况下是将热点数据所在的页保留,将不常访问的数据所在的页移除,InnoDB 借由 LRU 算法实现了一个链表。

2.3.1 简单 LRU 链表

如果使用传统的 LRU 链表,当需要访问某个页时可以按照如下方式处理链表:

  1. 如果该页不在 Buffer Pool 中,在把该页从磁盘加载到 Buffer Pool 中的缓冲页时,就把该缓冲页对应的控制块作为节点塞到 LRU 链表的头部。
  2. 如果该页已经被加载到 Buffer Pool 中则直接把该页对应的控制块移动到 LRU链表的头部。

即:只要使用到某个缓冲页就将该缓冲页加入到 LRU链表头部,这样 LRU 链表的尾部就是最近最少使用的页,当 Buffer Pool 中空闲缓冲页用完后直接淘汰尾部的页即可。

但这种使用存在问题:

  1. 加载到 Buffer Pool 中的页不一定被用到: InnoDB 存在预读情况,即 InnoDB 认为在执行当前请求时,可能会在后面读取某些页面,就预先将这些页面加载到 Buffer Pool中。这里的预读有分为两种情况:

    • 线性预读:如果顺序访问某个区的页面超过 innodb_read_ahead_threshold (默认56)系统变量的值就会触发一次异步读取下一个区中全部的页面到 Buffer Pool中的请求。
    • 随机预读:当某个区的13个连续的非常热的页(非常热的页:非常热区域的页,下面有介绍)都被加载到 Buffer Pool中,无论这些页面是否是顺序读取,都会触发一次异步读取本区中所有其他页面到 Buffer Pool 中的请求。随机预读可以通过 innodb_random_read_ahead 变量来控制是否开启,默认关闭。
  2. 如果有非常多使用频率偏低的页被同时加载到 Buffer Pool 中,则可能会把那些使用频率非常高的页从 Buffer Poll 中淘汰掉:在执行一些大数据量操作时(如全表扫描)会一次性读取很多页,如果Buffer Pool 容量不足则会将其他页面挤出 LRU 链表。此时全表扫描所加载的页可能并不是热点页,而真正的热点页已经被排挤出 LRU 链表。

2.3.2 优化后的 LRU 列表

因为上述问题,InnoDB 的 LRU 链表做了一定的优化处理:InnoDB 将 LRU链表划分为两部分,访问频率高的数据称为热数据,或者称为 young 区域,另一部分访问频率低的数据称为冷数据,或者称为 old 区域。默认情况 young区域占比是 3/8 (37%),这个比例可以通过 innodb_old_blocks_pct 调整。

经过上述调整后 LRU 链表便可以上面提到的问题做处理了:

  1. 针对预读的页面可能不进行后续访问的优化:当磁盘上某个页面在初次加载到 Buffer Pool 中的某个缓冲页时,该缓冲页的控制块会被放到 old 区域的头部,这样一来,预读到 Buffer Pool 却不进行后续访问的页面就会逐渐从 old 区域逐出,而不会影响 young 区域中的热点数据。
  2. 针对全表扫描时,短时间内大量使用频率低的页面的优化:在对某个处于 old 区域的缓冲页进行第一次访问时,就在它对应的控制块中记录下着访问时间,如果后续的访问时间与第一次的访问时间在某个时间间隔(通过 innodb_old_blocks_time 控制,默认是1s)内,那么该页面就不会从 old 区域移动到 young 区域的头部,否则将他移动到 young 的头部。对于一个页来说,当它从磁盘加载到 LRU 链表中的 old 区域的某个页时,如果第一次和最后一次访问该页面的时间间隔小于1s,则该页不会加入到 young 区域,而对于一次全表扫描,多次访问一个页面(即读取同一个页面中的多条记录)的时间不会超过1s。
2.3.3 更进一步的优化

对于 LRU 链表中的 young 区域的缓冲区来说,每次访问一个缓冲页就要将其移动到 LRU链表的头部开销着实有点大,因为在 young 中的都是热点数据,频繁对 LRU 链表操作并不是明智之举。因此还存在一些优化策略:如只有被访问的缓冲页位于 young 区域1/4 的后面才会被移动到 LRU 链表的头部,这样可以降低调整 LRU 比链表的频率,提高性能。即 在 young 区域 前 1/4 的区域称为 常热区域,在 young 区域 后 3/4 的区域称为 非常热区域

3. 脏页的刷新

InnoDB 后台有专门的线程负责每隔一段时间就把脏页刷新到磁盘,这样可以保证不影响用户线程处理正常情况,刷新方式主要有如下两种:

  1. 从 LRU 建表的冷数据中刷新一部分页面到磁盘 :后台线程会定时从 LRU 链表尾部开始扫描一些页面,如果发现了脏页则将其刷新到磁盘。这种刷新页面的方式称为 BUF_FLUSH_LRU。(缓冲页对应的控制块中存储了该缓冲页是否是脏页的信息,所以扫描 LRU链表可以很轻松获取到某个缓冲页是否是脏页)

  2. 从 Flush 链表中刷新一部分页面到磁盘 :后台线程会定时从 Flush 链表尾部开始扫描一些页面,刷新的速率取决于当时系统是否繁忙,这种刷新方式称为 BUF_FLUSH_LIST。

  3. 有时候后台线程刷新脏页的进度比较慢,导致用户线程在准备加载一个磁盘页到 Buffer Pool 中时没有可用的缓冲页,这时就会尝试查看 LRU 链表尾部,看是否存在可以直接释放掉未修改缓冲页。如果没有则将LRU 链表尾部的一个脏页同步刷新到磁。这种将单个页面刷新到磁盘中的刷新方式称为 BUF_FLUSH_SINGLE_PAGE。在系统特别繁忙时,也可能出现用户线程从 Flush 链表中刷新脏页的情况。

4. 多个 Buffer Pool 实例

Buffer Pool 本质上是向 OS 申请的一块连续的内存。在多线程环境下,访问 Buffer Pool 中的各种链表都需要加锁,在 Buffer Pool 特别大且线程并发量特别高的情况下, 单一的 Buffer Pool 可能会影响到请求速度,因此InnoDB 提供了 innodb_buffer_pool_instances 参数可以修改 Buffer Pool 的数量,需要注意的是InnoDB 规定当 innodb_buffer_pool_size 小于 1G 时设置多个实例时无效的,InnoDB 会默认将 innodb_buffer_pool_instances 改为 1。


三、重做日志缓冲(redo log buffer)

InnoDB 的内存区域除了缓冲池外,还有重做日志缓冲(redo log buffer)。InnoDB 首先将重做日志信息先放入到这个缓冲区。然后按照一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置很大,因为一般情况下会有后台线程(默认每秒一次)将其刷新到日志文件中,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可。

通常情况下 8M 的重做日志缓冲池足以满足绝大部分场景,因为重做日志在以下情况会刷新到重做日志文件

  1. MySQL 主线程 每秒将重做日志缓冲刷新到重做日志文件。
  2. 每个事务提交时会将重做日志缓冲刷新到重做日志文件。
  3. 当重做日志缓冲池剩余空间小于一半时会将重做日志缓冲刷新到重做日志文件。

四、参考内容

书籍:《MySQL是怎样运行的——从根儿上理解MySQL》、《MySQL技术内幕 InnoDB存储引擎 》

如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/684895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【最新鸿蒙应用开发】——Want信息载体

信息传递载体Want 1、概述 上一章节我们学习了UIAbility组件 【最新鸿蒙应用开发】——一篇搞懂什么是UIAbility-CSDN博客 ,其中组件间的交互传递信息的媒介就是Want,本章节我们来更加深入学习Want的相关知识。 Want是一种对象,用于在应用组…

性能狂飙:SpringBoot应用优化实战手册

在数字时代,速度就是生命,性能就是王道!《极速启航:SpringBoot性能优化的秘籍》带你深入SpringBoot的内核,探索如何打造一个飞速响应、高效稳定的应用。从基础的代码优化到高级的数据库连接池配置,再到前端…

恭喜!Z医生喜提世界顶级梅奥诊所访问学者邀请函

➡️【院校简介】 梅奥诊所(Mayo clinic),于1863年在美国明尼苏达州罗彻斯特创立,是全球规模最大的综合性非营利医生执业组织。它是以不断创新的医学教育和世界领先的医学研究为基础,建立起来的全美规模最大、设备最先…

YashanDB携手宏杉科技助力国产软件生态发展

近日,深圳计算科学研究院崖山数据库系统YashanDB与宏杉科技系列存储、系列服务器与数据库一体机等多款产品顺利完成兼容性互认证。经严格测试,双方产品完全兼容,稳定运行,共同提供高效、稳定、安全的国产软硬件一体化解决方案&…

Spring异步任务@Async的默认线程池执行器是如何初始化的

Spring异步任务Async的默认线程池执行器,是从哪里来?是如何初始化的? 结论先行 异步任务Async的默认线程池执行器是通过TaskExecutionAutoConfiguration#applicationTaskExecutor自动注入的。 异步任务的线程池执行器是如何初始化的&#…

查看Linux端口占用和开启端口命令

查看端口的使用的情况 lsof 命令 比如查看80端口的使用的情况 lsof -i tcp:80列出所有的端口 netstat -ntlp查看端口的状态 /etc/init.d/iptables status开启端口以开启端口80为例。 1 用命令开启端口 iptables -I INPUT -p tcp --dport 80 -j accpet --写入要开放的端口/…

【Vue】scoped解决样式冲突

默认情况下写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件 一、代码示例 BaseOne…

龙迅#LT8711H支持TYPE-C/DP/EDP转HDMI功能应用,分辨率支持 1080p@60Hz,芯片内置固件!

1. 概述 LT8711H是一款高性能 Type-C/DP1.2/EDP 转 HD-DVI1.3 转换器,设计用于将 USB Type-C 源或 DP1.2 源连接到 HD-DVI1.3 接收器。 该LT8711H集成了符合 DP1.2 标准的接收器和符合 HD-DVI1.3 标准的发射器。此外,还包括两个用于 CC 通信的 CC 控制器…

Linux服务器扩容及磁盘分区(LVM和非LVM)

Linux扩容及磁盘分区(LVM和非LVM) 本文主要介绍了阿里云服务器centos的扩容方法:非LVM分区扩容方法(系统盘),以及磁盘改LVM并分区(数据盘)。主要是ext4文件系统及xfs磁盘scsi MBR分…

springcloud Feign调用拦截器(统一处理拷贝请求头实现透传信息、内部调用鉴权、打印feign调用)

springcloud Feign调用拦截器(统一处理拷贝请求头实现透传信息、内部调用鉴权、打印feign调用日志) 实现接口Feign.RequestInterceptor 实现接口 feign.RequestInterceptor 并注入到IOC容器即可生效 示范代码如下 拷贝请求头,将原请求信…

Redis:Redis的数据类型介绍

Redis 支持多种数据类型,每种数据类型都有其特定的用途和优势。以下是 Redis 中主要数据类型的介绍: 1. String(字符串) 介绍:最基本的 Redis 数据类型,通常用于缓存和存储经常需要读取的数据。 示例&am…

病理级Polymer酶标二抗IHC试剂盒上线!

免疫组织化学 Immunohistochemistry,lHC 是利用抗体与抗原特异性识别原理,对组织样本中的抗原进行定位/定性分析的实验技术。组织切片保留了样品的解剖学结构特征,从而可以高分辨率地显现蛋白在细胞,甚至细胞器中的定位。基于以上特性&…

开源Mamba-2性能狂飙8倍!多个Mamba超强进化体拿下顶会

MambaOut的热度刚过去没多久,Mamba-2就带着它狂飙8倍的性能炸场了。 Mamba-2的核心层是对Mamba的选择性SSM的改进,同等性能下,模型更小,消耗更低,速度更快。与Mamba不同,新一代的Mamba-2再战顶会&#xff…

JVM垃圾收集器和性能调优

目标: 1.JVM垃圾收集器有哪几种? 2.CMS垃圾收集器回收步骤。 一、JVM常见的垃圾回收器 为什么垃圾回收的时候需要STW? 标记垃圾的时候,如果不STW,可能用户线程就会不停的产生垃圾。 1.1 单线程收集 Serial和SerialOld使用单…

windows架设NTP时间服务器进行时间同步

一、windows架设NTP时间服务器 1.win11更改注册表 winR输入regedit 2.HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config,找到Config目录,双击Config目录下的AnnounceFlags,设为5。 3.HKEY_LOCAL_MACHINE\SYSTEM\Current…

鸿蒙全栈开发-一文读懂鸿蒙同模块不同模块下的UIAbility跳转详解

前言 根据第三方机构Counterpoint数据,截至2023年三季度末,HarmonyOS在中国智能手机操作系统的市场份额已经提升至13%。短短四年的时间,HarmonyOS就成长为仅次于安卓、苹果iOS的全球第三大操作系统。 因此,对于鸿蒙生态建设而言&a…

前端--导出

这边记录我们公司后端做的导出接口和前端是如何对接的 这边的技术栈是: 1: react 2: fetch 第一步:简单封装--导出界面 import { DrawerForm } from ant-design/pro-components; import { CloseOutlined } f…

不会制作企业版电子书?学会这几个步骤就好啦!

公司安排你制作一本专业的电子书,不知道如何下手?别担心,今天LookLook同学就来给大家分享一下如何轻松制作企业版电子书。参考这几个步骤,相信你一定能轻松搞定! 第一步:明确电子书的目标和受众 在开始制作…

【ai】DeepStream 简介

NVIDIA Metropolis 平台。 NVIDIA 大都会 利用视觉 AI 将来自数万亿物联网设备的数据转化为有价值的见解。 NVIDIA Metropolis 是一个应用程序框架、一套开发工具和合作伙伴生态系统,它将视觉数据和 AI 结合在一起,以提高各行各业的运营效率和安全性。它有助于理解数万亿个…

漏洞挖掘 | 验证码绕过

还是老规矩,开局一个登录框,中途漏洞全靠舔,先来研究一下这个登录窗口 很好,发现有验证码登录,先测试测试能不能并发 看来没有,只成功发送了两条,再看看验证码是不是4位 很好,是4位。…