【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存储引擎 》

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

B端产品无爆款,说有的都是忽悠和外行!

前言:网上经常有人讲运营,把C端那一套硬搬到B端,讲的自我陶醉,稍微有点常识的人就知道不能这么玩。 一、什么是B端和C端 B端(Business-to-Business)是指面向企业客户的市场和产品。B端产品或服务主要是为…

Elastic Connectors:增量同步对性能的影响

作者:Artem Shelkovnikov Elastic 连接器是一种 Elastic 集成,可将数据从原始数据源同步到 Elasticsearch 索引。连接器使你能够创建可搜索的只读数据源副本。 有许多连接器支持各种第三方,例如: MongoDB各种 SQL DBMS&#xff…

【大学物理实验】速通双语版

0首先,我们要学什么?outlook! 1measurement 2system error&random error 3significant figures 4uncertainty of direct measurement and indirect measurement 5data processing 1 measurement Important points to remember&#…

从Multisim到Proteus,再到SmartEDA:电子设计软件的进化之旅

在电子设计领域,软件工具的演进犹如一条璀璨的轨迹,记录着科技进步的每一个步伐。从Multisim的初创,到Proteus的崛起,再到如今SmartEDA的崭露头角,这些电子设计软件不仅极大地提升了设计效率,更推动了整个行…

fly-barrage 前端弹幕库(6):实现人像免遮挡

项目官网地址:https://fly-barrage.netlify.app/; 👑🐋🎉如果感觉项目还不错的话,还请点下 star 🌟🌟🌟。 Gitee:https://gitee.com/fei_fei27/fly-barrage&a…

vue3+vant4 二次封装IndexBar城市列表组件以及性能优化

前言 二次封装城市列表以及对静态资源的引入做异步引入优化。 版本号: vue3.3 vant4 效果图 数据源 城市列表 city.json 永久网盘链接 链接:https://pan.baidu.com/s/10E-b441-4P7mjvomlJhm0g 提取码:m92c 字段大致讲解 indexList :每个字…

开窗函数!

开窗函数(Window Function)是SQL中的一种高级功能,允许你在一组相关行(一个“窗口”)上执行聚合操作,而不像传统聚合函数(如SUM(), AVG(), COUNT())那样将所有匹配行合并成单个汇总行…

大数据的数据采集

大数据采集是指从各种来源收集大量数据的过程,这些数据通常是结构化或非结构化的,并且可能来自不同的平台、设备或应用程序。大数据采集是大数据分析和处理的第一步,对于企业决策、市场分析、产品改进等方面具有重要意义。以下是大数据采集的…

Vue3项目炫酷实战,检测密码强度值

在前端项目开发中,确保用户密码的强度是保护账户安全的重要措施。本文将演示如何使用Vue 3实现一个简单的密码强度检测功能。通过实时反馈,帮助用户创建更安全的密码,从而提升整体系统的安全性。无论您是前端开发新手还是经验丰富的开发者&am…

与5月汽车销量共舞:MK米客方德SD NAND助力车载T-box

上周末,各家车企集体公布5月销量数据,新能源车龙头比亚迪单月销量遥遥领先,数据显示,比亚迪5月新能源汽车销量33.18万辆,上年同期销量24.02万辆。1至5月,比亚迪新能源汽车销量累计127.13万辆,同…

重生之 SpringBoot3 入门保姆级学习(17、整合SSM)

重生之 SpringBoot3 入门保姆级学习&#xff08;17、整合SSM&#xff09; 4、数据访问4.1 整合 ssm 4、数据访问 4.1 整合 ssm pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" …

指针的认识(指针变量类型意义、指针运算)

目录 一、指针变量类型的意义 a.通过指针的解引用理解类型意义 b.void* 类型指针 c.const修饰的指针变量 c.1 const放在*号左边​编辑 c.2 const放在*号右边 二、指针运算 a.指针-整数 ​编辑 b.指针 - 指针 ​编辑 c.指针的关系运算 一、指针变量类型的意义 指针变…

【学习】软件测试中如何进行Web网页兼容性测试

在数字时代&#xff0c;Web网页作为信息传递和交流的重要平台&#xff0c;其稳定性和用户体验至关重要。如同一位匠人细致打磨他的工艺品&#xff0c;开发者亦需精心测试网页的兼容性&#xff0c;确保其在各种设备和浏览器上的表现无懈可击。今天&#xff0c;我们就来探讨如何对…

SpringBoot+百度地图+Mysql实现中国地图可视化

通过SpringBoot百度地图Mysql实现中国地图可视化 一、申请百度地图的ak值 进入百度开发者平台 编辑以下内容 然后申请成功 二、Springboot写一个接口 确保数据库里有数据 文件目录如下 1、配置application.properties文件 #访问端口号 server.port9090 # 数据库连接信息 spr…

如何高效管理自己的时间,可以从这几个方向着手

如果你是上班族&#xff0c;天选打工人&#xff0c;你的绝大多数时间都属于老板&#xff0c;能够自己支配的时间其实并不多&#xff0c;所以你可能察觉不到时间管理的重要性。 但如果你是自由职业者或者创业者&#xff0c;想要做出点成绩&#xff0c;那你就需要做好时间管理&am…

NVIDIA发布重磅AI创新,黄仁勋在COMPUTEX大会预示计算未来

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Facebook开户|Facebook广告投放指南

家人们中午好~今天的文章由我们帅气逼人的大帅哥Zoey为大家分享&#xff08;狗头&#xff09;~有想要通过Facebook广告掘金的家人们&#xff01;今天就跟大家分享一下Facebook广告投放的底层逻辑和实用技巧&#xff0c;帮助大家少走弯路&#xff0c;快速入门~ 基础知识&#x…

韶关学院携手泰迪智能科技“见习研学”活动圆满结束

为进一步深化校企合作&#xff0c;落实高校应用型人才培养。5月31日&#xff0c;韶关学院与广东泰迪智能科技股份有限公司联合开展学生企业见习活动。专业教师林思思以及来自韶关学院140名学生参与此次见习活动&#xff0c;泰迪智能科技培训业务部经理钟秋平、校企合作经理吴桂…

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

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

【Java数据结构】详解Stack与Queue(二)

&#x1f512;文章目录: 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2.栈的应用场景 2.1逆序打印链表 2.2逆波兰表达式求值 2.3括号匹配 2.4出栈入栈次序匹配 2.5最小栈 3. 栈 虚拟机栈 栈帧的区别 4.总结 1.❤️❤️前言~&#x1f973…