【MySQL】深入解析 Buffer Pool 缓冲池

news2024/9/28 23:22:44

文章目录

    • 1、前置知识
      • 1.1、Buffer Pool介绍
      • 1.2、后台线程
        • 1.2.1、Master Thread
        • 1.2.2、IO Thread
        • 1.2.3、Purge Thread
        • 1.2.4、Page Cleaner Thread
      • 1.3、重做日志缓冲池
    • 2、Buffer Pool 组成
      • 2.1、数据页
      • 2.2、索引页
      • 2.3、undo页
      • 2.4、插入缓冲
      • 2.5、锁空间
      • 2.6、数据字典
      • 2.6、自适应哈希索引
    • 3、Buffer Pool 内存管理
      • 3.0、控制块
      • 3.1、Free List
      • 3.2、Flush List
      • 3.3、LRU List

1、前置知识

1.1、Buffer Pool介绍

MySQL数据库具有可拔插存储引擎,其中最常用的是InnoDB,而Buffer Pool缓冲池InnoDB存储引擎中特有的内存结构,MySQL向操作系统内存申请一块内存空间用于Buffer Pool缓冲池使用,因为硬盘和内存性能差距大,所以Buffer Pool缓冲池用于协调CPU速度和硬盘速度的鸿沟,Buffer Pool大幅度提升MySQL数据库的读写性能。

按照我们的惯性思维,这里会有一个疑问:不都说MySQL的数据是基于硬盘存储吗,为什么这里会提到Buffer Pool缓冲池内存这概念?

MySQL当然是通过硬盘持久化存储Buffer Pool 并不是 MySQL 真正意义上存储数据的单元载体。
MySQL仅仅是借助Buffer Pool提升读写性能,毕竟内存的访问速度要比硬盘快得多!这并不冲突。

我们进行数据的查询操作,MySQL并不是直接从硬盘文件中查找对应的数据信息,会先查看Buffer Pool中是否有想要查询数据。如果有,直接返回给用户;如果没有,去硬盘中的查询想要的数据。查询到结果后会同步到Buffer Pool中,下次用户再次发起查询就不用访问磁盘了,修改操作也是同理(先操作Buffer Pool中的数据,然后数据刷入硬盘的文件中,有点像Redis),我们先站在操作系统维度看看 Buffer Pool 在内存中的样貌:
在这里插入图片描述

Buffer Pool缓冲池数据页,硬盘中MySQL表数据加载到Buffer Pool中就是通过数据页来存放的,Buffer Pool默认大小128MBInnoDB存储引擎已经将硬盘中的数据划分为一个个,默认大小16KB,通过为基本单位,进行硬盘内存之间的交互。

上面提到了查询、修改等SQL操作,无论是Buffer Pool将修改的页刷盘到硬盘,还是从硬盘加载到Buffer Pool,都是以数据页为单元进行操作的,而不是操作中的某几行数据。

这里有个注意点,Buffer Pool缓冲池并不是只有一个的,可以申请多个内存区域作为缓冲池同时工作。

1.2、后台线程

之前提到一个概念叫做刷盘,意思是Buffer Pool中缓存页数据会异步刷新到硬盘中,保证了数据的一致性,后台线程的主要作用就是对缓冲池中的页进行进行操作。InnoDB存储引擎后台线程主要有以下几种:
在这里插入图片描述

1.2.1、Master Thread

该线程主要用于将Buffer Pool 缓冲池中的数据进行刷盘,保证数据一致性。主要主责包括:脏页刷盘、插入缓冲合并、undo页回收等。

1.2.2、IO Thread

该线程主要用于处理AIO(Async IO)请求回调,因为InnoDB存储引擎中存在大量的异步IO操作,IO Thread可以极大数据库性能。

1.2.3、Purge Thread

事务提交之后,undo页就没有任何存在的意义了,该线程主要职责就是回收无用的undo页

上面1.2.1 Master Thread中提到,Master Thread主要职责就包括了回收undo页,但是后续InnoDB版本开始将部分purge操作交给Purge Thread来完成,减少Master Thread的工作压力,提升性能。也就是说回收undo页功能Master ThreadPurge Thread都具备,该线程就是为了替Master Thread分担回收undo页的工作压力。

1.2.4、Page Cleaner Thread

该线程也是为Master Thread分担工作压力,提升数据库性能。不过Page Cleaner Thread分担了什么压力呢?脏页刷盘操作。

1.3、重做日志缓冲池

硬盘中存在重做日志文件,主要用于故障恢复,保证MySQL事务的持久性,重做日志缓冲池就是用于存放重做日志信息,然后按照一定频率刷盘重做日志文件中,常用于数据库的故障恢复场景,这并不是本文章的重点。

2、Buffer Pool 组成

2.1、数据页

当我们进行查询的数据不在缓冲池中时,就会将磁盘中的数据对应的页加载到Buffer Pool中,这就是数据页。当我们对数据页内容进行修改,此时数据页就会变成脏页,而不是直接操作硬盘中的文件页,只需要将脏页刷新到磁盘中,这样通过为单位交互性能好很多。
在这里插入图片描述

2.2、索引页

Buffer Pool缓冲池中,不仅会存放数据页,还会存放索引页

之所以这样,是因为我们不能保证每次查询操作都能从缓冲池的数据页中拿到想要的结果,此时就需要对磁盘中数据文件进行IO访问操作。如果本次的查询操作命中了索引,我们又该如何知道索引的根节点到底在磁盘中的哪个位置呢?这个时候就需要索引页来帮助我们,当MySQL实例启动时,就会将数据库中的索引根节点放入到缓冲池的索引页中,当我们的查询SQL命中了索引,就不需要在整个磁盘中查找对应的索引根节点了!

2.3、undo页

undo页主要记录事务回滚操作信息,常用于事务回滚操作。

事务如何通过undolog进行回滚操作呢?这个很好理解,我们只需要在undolog日志中记录事务中的反向操作即可,例如:

事务进行insert操作,undolog记录delete操作
事务进行delete操作,undolog记录insert操作
事务进行update操作(a改为b),undolog记录update操作(b改为a)

2.4、插入缓冲

插入缓冲只针对非聚集、不唯一索引页增、删、改操作。

当我们对非聚集、不唯一索引页进行插入、修改操作时,不是直接操作索引页,而是先判断当前索引页是否在Buffer Pool缓冲池中,如果在直接操作索引页即可,如果不在就放入Insert Buffer对象中,然后以一定频率进行插入缓冲和辅助索引页合并操作,大大提升非聚集索引操作性能!

那为什么聚集索引或者说主键索引不需要插入缓冲?因为主键索引插入操作是按照主键顺序递增的,属于顺序插入,不需要随机读取硬盘,性能很快。

2.5、锁空间

锁空间就是专门用来存储锁结构、并发事务的链表的一块内存区域,这里不过多介绍。

2.6、数据字典

MySQL数据库启动时,会自动从硬盘中将系统表相关信息加载到Buffer Pool缓冲池中,有了数据字典,这样当我们使用show indexshow tables相关命令就能查到表、索引相关的信息,主要分为以下:

SYS_TABLES:存储所有InnoDB表信息。
SYS_COLUMNS:存储所有用户定义的表字段信息。
SYS_INDEXES:存储所有InnoDB引擎表索引信息。
SYS_FIELDS:存储所有索引的定义信息。

2.6、自适应哈希索引

默认情况下,我们的索引页采用B+Tree的结构,大幅度提高我们对数据库的查询性能,虽然性能已经很好了,但是自适应哈希索引的性能棒不得了!O(1)时间复杂度,查询性能非常高。

自适应哈希索引不需要我们主动人为干涉,它是InnoDB自动生成的,自适应哈希索引针对是热点索引页,而不是整张表,并且生成的条件也比较苛刻。当我们对某个索引页连续的访问模式条件一样,访问模式例如:

where a = xxx
where a = xxx and b = yyy

上面举例这两种访问模式不能交替执行,否则也不会生成自适应哈希索引,那何时自动生成呢,有以下两种情况:

以某个模式访问100次
以某个模式访问 n 次(n = 页中记录 / 16)

3、Buffer Pool 内存管理

3.0、控制块

InnoDB在操作系统中为Buffer Pool缓冲池申请创建了一块连续的内存,内存被划分成一块块缓冲页(之前提过缓冲池是以页为基本单位与磁盘进行交互),InnoDB存储引擎为缓冲池中每个缓冲页都生成了一个控制块,一对一关系。

控制块中记录了数据页所属的表空间、页号、缓冲页地址、链表节点指针等信息。控制块缓存页关系图如下:发现图中有个内存碎片,这是因为缓冲池剩余空间不够一对控制块和缓存页的大小,这点剩余内存空间就被称为内存碎片
在这里插入图片描述

3.1、Free List

Buffer Pool缓冲池内存被划分为一个个,但并不是所有都被使用,有一些是处于空闲状态的(没存数据),这种空闲页会被Free List进行管理,方便快速查找使用。

当硬盘中的页刷入到Buffer Pool缓冲池中时,就会从Free List中查找是否有空闲页,如果有,就将空闲页Free List中取出使用(移除);如果没有,就会使用后续提到的LRU List列表的尾部的数据页。下图中头节点解释:

head:指针,指向 Free List 的第一个控制块。
ail:指针,指向 Free List 的最后一个控制块。
count:数字,记录 Free List 的节点数量。

在这里插入图片描述

3.2、Flush List

之前提到过脏页刷盘这个操作,所谓脏页就是缓冲池缓存页中内容发生了改变(修改、删除、新增),此时这个该页就称为脏页。脏页数据和磁盘中文件数据是不一致的,需要后台线程将数据异步刷新到磁盘中。这些脏页的管理就需要Flush List,结构图跟3.1 Free List大同小异,这里就不重复画了。

3.3、LRU List

知道了Free List维护空闲页Flush List维护脏页,那么LRU List维护的是什么页?

LRU List用来管理已经读取的页,所以当数据库刚启动时,LRU List也是空的,这时候的空闲页都在Free List中,当需要从硬盘中加载数据页到Buffer Pool时,就会从Free List查找是否有空闲页可以使用,如果没有空闲页就根据LRU算法淘汰LRU List尾部页,将内存空间分配给新页。

硬盘中的页加载到缓冲池中,没有任何修改操作,那就说这个缓冲页是干净的(干净页),或者说脏页数据刷盘到磁盘后,就变成了干净页。不过有一点需要强调,当我们对干净页进行修改操作时,也就是它变成了脏页,此时脏页也不会从LRU List中移除,这个脏页将会同时存在于LRU ListFlush List中。

关于脏页是否同时在LRU、Flush List中存在,这里有些争议,有些人认为脏页不在LRU List中记录,只在Flush List中记录;
不过《MySQL技术内幕 InnoDB存储引擎》这本书中介绍的是:脏页既存在于LRU List,也存在于Flush List

LRU List管理缓存页是通过LRU算法,就是说访问频率低(最近最少使用)的缓存页将会放到LRU List列表尾部,访问频率比较高的热点页将会放到LRU List首部,当可用的空闲页不足时,就会淘汰LRU List链表末尾的数据页。我们先来看下LRU List大致是什么样子:
在这里插入图片描述
LRU ListLRU算法跟常规的LRU算法是有区别的,InnoDB之所以使用特殊的LRU算法,主要是考虑到传统的LRU算法有这两个问题:

  • 预读无效
  • Buffer Pool污染

预读的意思是 Buffer Pool 在加载数据页时,会把它相邻的数据页一起加载到缓冲池中,目的是减少了磁盘IO操作。不过常规LRU算法会将预读的数据页也放置到LRU List头部,这样可能出现预读数据页几乎不会使用到(大大降低LRU List的使用性能)。

Buffer Pool污染大概也是这个意思,如果偶尔做一次大数据量的表查询操作(全表扫描),直接出现许多不常用数据页在LRU List头部,导致本身的热点页被移除。降低了LRU List使用性能。

针对以上常规LRU算法所带来的问题,LRU List是用了特殊LRU算法。上图中可以看到midpoint,通过midpoint为分界线,将midpoint左侧数据页区域称为NEW区,右侧称为OLD区。NEW区域的数据页是经常使用、访问的,这些数据页我们称之为热点页midpoint位于LRU List链表的5/8处(37 : 63),这个比例可以通过参数innodb_old_blocks_pct调整,这样我们最新访问的数据页不会直接放到NEW区域的头部,而是放到OLD区域的头部。

那么什么时候会从OLD区移动到NEW区呢?InnoDB存储引擎通过一个时间参数innodb_old_blocks_time控制页读取到midpoint位置时,等待多久才会加入到NEW区,这个时间默认为1000ms。如果后续的访问时间与第一次访问的时间不在这个时间间隔内,那么该缓存页就会移动到 NEW 区域的头部,这就是LRU List管理缓存页的方式。

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

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

相关文章

金三银四求职攻略:如何在面试中脱颖而出

随着春天的脚步渐近,对于众多程序员来说,一年中最繁忙、最重要的时期也随之而来。金三银四,即三月和四月,被广大程序员视为求职的黄金时段。在这段时间里,各大公司纷纷开放招聘,求职者们则通过一场又一场的…

搜索算法(算法竞赛、蓝桥杯)--双向DFS+二分查找

1、B站视频链接&#xff1a;B26 双向DFS 送礼物_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; int n,m; int g[46];//存储所有物品的质量 int w[1<<23];//存储所有能凑出来的重量 int ans,cnt;//w的个数是cnt//搜索第u个数&#xff0c;和为s; …

Java数据类型(八种基本数据类型 + 四种引用类型)、数据类型转换

1.总览 Java的数据类型只有两大类&#xff1a;8大基本数据类型与引用数据类型。其中基本数据类型又被称为值类型 基本数据类型&#xff1a;6种数字类型&#xff08;byte/short/int/long/float/double&#xff09;、1种字符型&#xff08;char&#xff09;、1种布尔型&#xff…

Java中常见延时队列的实现方案总结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

爬虫的一些小技巧总结

一、在爬虫中&#xff0c;爬取的数据类型如下 1.document:返回的是一个HTML文档 2.png:无损的图片&#xff0c;jpg:压缩后的图片,wbep:有损压缩&#xff0c;比png差&#xff0c;比jpg好 3.avgxml图像编码字符串 4.script:脚本文件&#xff0c;依据一定格式编写的可执行的文…

高性能MySQL 第4版

第一章MySQL架构 MySQL提供了多种锁的颗粒度&#xff0c;每种MySQL存储引擎都可以实现自己的锁策略和锁力度。 行级锁是在存储引擎而不是在服务器中实现的。 隔离界别 READ UNCOMMITTED - 脏读 在事务中可以可以查看到其他事务中还没有提交的修改。实际中很少用。 READ C…

(C语言)qsort函数模拟实现

前言 我们需先了解qsort函数 qsort函数详解&#xff1a;http://t.csdnimg.cn/rTNv9 qsort函数可以排序多种数据类型&#xff0c;很是神奇&#xff0c;这是为什么&#xff0c;我们在里模拟实现这样的功能 目录 1. qsort函数模拟实现 2. 我们使用bubble_sort函数排序整形数…

微星H510M PLUS V3主板安装纯Ubuntu系统安装或启动界面黑屏问题

Ubuntu系统安装或启动界面黑屏问题 1 前言2 原因分析3 解决方法3.1 安装界面黑屏3.2 启动界面黑屏3.3 安装显卡驱动 1 前言 我自己组装了一台台式机&#xff0c;配置如下&#xff1a; 主板&#xff1a;微星H510M PLUS V3显卡&#xff1a;英伟达RTX 2060内存&#xff1a;金百达…

文件的顺序读写函数举例介绍

目录 例1&#xff1a;&#xff08;使用字符输出函数fputc&#xff09;例2&#xff1a;&#xff08;使用字符输入函数fgetc&#xff09;例3&#xff1a;&#xff08;使用文本行输出函数fputs &#xff09;例4&#xff1a;&#xff08;使用文本行输入函数fgets &#xff09;例5&a…

微信小程序屏蔽控制台黄色提示信息

我们很多时候 一个小程序 啥都没有 终端就一直报一些黄色的警告 可以打开项目的 project.config.json 找一下setting 下面有没有 checkSiteMap 字段 如果没有加一个 如果有 直接将值改为 false 这样 再运行 就不会有这个黄色的提示信息了

牛客网C++专项题目整理(1)

1. 若有定义语句:char s[3][10],(*k)[3],*p;则以下赋值语句错误的是 1.p s; 2.p k; 3.p s[0]; 4.k s; 答案&#xff1a;124 char s[3][10] s 是数组指针&#xff0c;类型为char (*)[3]&#xff0c;所指向的每个数组长度为10; char (*k)[3] k是一个数组指针&a…

Windows下用crashRpt让C++程序崩溃自动生成dump

背景 我们的Windows c程序如果在客户或者没有代码调试的环境下崩溃了。我们只能从机器异常报错里得知寥寥无几的信息&#xff0c;如果程序崩溃时&#xff0c;能自动触发当前堆栈信息的收集&#xff0c;那么对于开发人员复现BUG就尤为重要 CrashRpt CrashRpt主要功能 1.崩溃报…

【Java基础系列】时间戳

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

政安晨【示例演绎虚拟世界开发】(五):从制作一个对战小游戏开始(Cocos Creator 《击败老大》)(第二段)

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: AI虚拟世界大讲堂 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 现在我们已经学会了如何向场景中添加图片&#xff0c;接下来继…

Django路由层(反向解析、路由分发、命名空间、路径转换)

Django路由层&#xff08;反向解析、路由分发、命名空间、路径转换&#xff09; 目录 Django路由层&#xff08;反向解析、路由分发、命名空间、路径转换&#xff09;路由配置反向解析前端用法后端用法 反向解析的本质路由分发用法示例 分发时遇到的问题解决方案1解决方案2 命名…

过于老旧的pytorch_ssim包 请从github下载源码

有些冷门算法真的不要随便pip&#xff0c;有可能下载到史前版本…最好还是找源代码 汗 今天要用到SSIM损失函数&#xff0c;从网上简单看了一下原理就想测试一下&#xff0c;偷了一下懒就直接在命令行输入pip install pytorch_ssim了&#xff0c;结果报了一堆错误&#xff08;汗…

vue3编写H5适配横竖屏

具体思路如下&#xff1a; 1、监听浏览器屏幕变化&#xff0c;通过监听屏幕宽高&#xff0c;辨别出是横屏&#xff0c;还是竖屏状态 在项目的起始根页面进行监听&#xff0c;我就是在App.vue文件下进行监听 代码如下&#xff1a; <template><RouterView /> <…

【MySQL】:约束全解析

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 约束概述二. 约束演示三. 外键约束3.1 介绍3.2 语法3.3 删除/更新行为 &…

Ribbon负载均衡(黑马学习笔记)

上Eureka一节中&#xff0c;我们添加了LoadBalanced注解&#xff0c;即可实现负载均衡功能&#xff0c;这是什么原理呢&#xff1f; 负载均衡原理 SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。 那么我们发出的请求明明是http://users…

文献阅读:The Unreasonable Effectiveness of Easy Training Data for Hard Tasks

文献阅读&#xff1a;The Unreasonable Effectiveness of Easy Training Data for Hard Tasks 1. 文章简介2. 方法介绍 1. 数据集难易度分析2. 模型训练前后变化 3. 实验考察 & 结论 1. 实验设计 1. 使用数据集2. 使用模型 2. 实验结果 1. 数据集难度分析2. 在Easy数据集下…