MySQL之深入InnoDB存储引擎——Buffer Pool

文章目录

    • 一、空闲链表的管理
    • 二、缓冲页的哈希处理
    • 三、Flush链表的管理
    • 四、LRU链表的管理
    • 五、脏页刷新
    • 六、多Buffer Pool实例

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓冲池中,在下一次读取相同的页时,首先判断该页是否存在缓冲池中,如果存在则被命中,直接读取,否则读取磁盘上的页。

Buffer Pool对应的一片连续的内存被划分为若干个页面,页面大小和InnoDB表空间使用的页面大小一致,默认都是16KB。为了更好地管理Buffer Pool中的这些缓冲页,InnoDB为每个缓冲页都创建了一个控制块,其中包含该页所属的表空间编号、页号、缓冲页在Buffer Pool中的地址、链表节点等信息。控制块位于Buffer Pool的内存的前部分,缓冲页位于Buffer Pool的后部分,两部分之间可能会存在碎片(如果剩余的空间不足够再分配一堆控制块和缓冲页则会产生)。

一、空闲链表的管理

在我们最开始启动MySQL服务器的时候,需要向操作系统申请Buffer Pool的内存空间,然后把它换分成若干对控制块和缓冲页。当然此时没有真实的磁盘页被缓存到Buffer Pool中,会随着程序的运行不断缓存。

当从磁盘上读取到一个页时,我们需要将其放置在空闲的缓冲页中。那我们就需要区分哪些页是空闲的,哪些页已经被使用了,因此引入了Free链表(空闲链表)。在Buffer Pool刚刚完成初始化之后,所有缓冲页对应的控制块都会加入到空闲链表中。而为了管理这个空闲链表,我们为其定义了一个基节点,其中包含了链表的头节点地址、尾节点地址以及当前链表中节点的数量等信息。需要注意的是,链表的基节点占用的内存空间并不包含在为Buffer Pool申请的一大片连续内存孔内,而是一块单独申请的内存空间。

有了空闲链表之后,每当需要从磁盘中加载一个页到Buffer Pool中时,就从空闲链表中取一个缓冲页对应的控制块,把页加载到控制块指向的缓冲页中,并把该控制块的信息给填上(即表空间、页号等)。

二、缓冲页的哈希处理

使用了Buffer Pool之后,我们访问某个页的数据时会先去缓冲池中查找该页是否已经加载到内存中,如果不存在才会去磁盘中加载。而为了快速判断目标页是否存在于缓冲池中,InnoDB采用的是哈希表的方式,将表空间号和页号作为键,缓冲页控制块作为值来进行构建。

三、Flush链表的管理

如果我们修改了Buffer Pool中某个缓冲页的数据,那其中的数据就和磁盘中的不同了,此时这个缓冲页变为了脏页。虽然我们可以在每次修改完都将其刷新到磁盘中对应的页上,但是这显然会严重影响程序的性能。因此InnoDB不会在每次修改完页都立刻刷库,而是在未来某个时间点进行刷新。

既然不是及时刷新,那我们在未来刷新时便需要知道哪些页是脏页。因此引入了Flush链表进行脏页的管理,当缓冲页被修改则将其控制块加入到链表中。

和空闲链表相同,它也有一个相同结构的基节点来保存这个链表的信息。显然当一个缓冲页的控制块在空闲链表中时则不可能在Flush链表中,反之同理。

四、LRU链表的管理

Buffer Pool对应的内存大小毕竟是有限的,随着程序的运行Free链表总归会用完。如果Free链表已经没有多余的空闲缓冲页了,那么只能将某些旧的缓冲页从Buffer Pool中淘汰,再将新的页放进来。Buffer Pool引入的初衷是为了想减少磁盘IO,因此自然希望淘汰掉的页面是最少使用的,而最近经常使用的页面不被淘汰,因此InnoDB采用的淘汰策略为LRU,即最近最少使用算法。以这个算法为核心,InnoDB维护了一个LRU链表,如果页不在Buffer Pool中,在把该页从磁盘加载到Buffer Pool的时候,就把该缓冲页对应的控制块作为节点添加到LRU链表的头部,如果使用到链表中的某个页时也将其移动到链表头部。之后当空闲缓冲页用完时,就从链表尾部进行淘汰。

但这种实现并不完善,它存在两个问题:

  1. 因为InnoDB提供了预读的功能,即InnoDB认为执行当前的请求时,可能会在后面读取某些页面,于是就预先把这些页面加载到Buffer Pool中。根据触发方式的不同,预读可分为以下两种:

    • 线性预读:InnoDB中有一个系统变量innodb_read_ahead_threshold,如果顺序访问某个区的页面超过这个系统变量的值,则会触发一次异步读取下一个区中所有页面的请求
    • 随机预读:如果某个区的13个连续的页面都被加载到了Buffer Pool中,无论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其他页面到Buffer Pool中的请求。这个功能默认不会开启
    • 预读到Buffer Pool中的页如果能够被成功的使用到,那自然可以极大地提高语句执行的效率。但是如果用不到,会导致LRU链表尾部的一些缓冲页很快就被淘汰掉,从而大大降低Buffer Pool的命中率
  2. 有的时候可能需要用到全表扫描,这个过程中需要访问的页面一般会特别多,那么就会导致LRU链表中很多页面都会被淘汰掉。但是这次全表扫描引进来的这些页面可能并不是热点数据,但是反而把热点数据从链表中赶跑了

为了避免以上两种情况把Buffer Pool中真正的热点数据淘汰,InnoDB将LRU分成两个部分:

  1. 一部分存储使用频率非常高的缓冲页,称为热数据或young数据
  2. 一部分存储使用频率不是很高的缓冲页,称为冷数据或old数据

LRU的划分是根据比例的,默认情况下old区域占LRU链表的37%。之后当磁盘的页面被初次加载到Buffer Pool中时,该缓冲页对应的控制块会放置到old区域的头部,而不再是整个链表的头部,这样就不会使得预读加载进来的页影响young区域中使用比较频繁的缓冲页。并且在对某个处于old区域的缓冲页进行第一次访问时,会在它对应的控制块中记录下整个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会从old区域移动到young区域头部。这样一来也可以防止全表扫描时除了将数据页加载到Buffer Pool,还会频繁读取页面中的记录导致仍然会淘汰掉热点数据的情况。

除了以上的改进,LRU还有进一步的优化策略。对于young区域的缓冲页来说,如果我们每次访问一个缓冲页都需要把它移动到LRU链表的头部,其实开销是比较大的,因为young区域都是热点数据。因此InnoDB规定只有被访问的缓冲页位于young区域1/4的后面时,才会被移动到LRU链表的头部。

五、脏页刷新

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

  1. 从LRU链表的冷数据中刷新一部分页面到磁盘:后台线程会定时从LRU链表尾部开始扫描一些页面。如果在LRU链表中发现脏页(缓冲页的控制块就会记录该缓冲页是否被修改),则把它们刷新到磁盘
  2. 从Flsuh链表中刷新一部分到磁盘:后台线程也会定时从Flush链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是否繁忙

当有线程在准备加载一个磁盘页到Buffer Pool中时没有可用的缓冲页时会尝试查看LRU链表尾部,看是否存在可以直接释放掉的未修改缓冲页。有时后台线程刷新脏页的进度比较慢,导致LRU链表尾部的脏页还没有被刷新,此时则不得不将LRU链表尾部的一个脏页同步刷新到磁盘中,这会降低用户请求的速度。

六、多Buffer Pool实例

在多线程环境下,访问Buffer Pool中的各个链表都需要加锁处理。在Buffer Pool特别大并且多线程并发量特别高的情况下,单一的Buffer Pool可能会影响请求的处理速度。所以在Buffer Pool特别大时,可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool都称为一个实例。它们之间是相互独立的,多线程并发时不会互相影响。

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

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

相关文章

docker xserver是什么

在Docker环境中,XServer是一个用于图形显示的X Window系统服务器。X Window系统是一种常用的图形用户界面(GUI)系统,允许在图形化桌面环境中运行应用程序。 当在Docker容器中运行需要图形界面的应用程序时,通常需要将…

用户权限提升Sudo

目录 前言 一、su的用法 二、sudo提权 总结 前言 sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,如halt,reboot,su等等。换句话说通过此命令可以让非root的用户运行只有root才有权限…

XML(eXtensible Markup Language)

目录 为什么需要XML? 一 XML语法 1.文档声明 2.元素 语法: 3.属性 4.注释 5.CDATA节 二 树结构 三 转义字符 四 DOM4J 1.XML解析技术 2.dom4j介绍 3.dom4j基本使用 XML 指可扩展标记语言(eXtensible Markup Language)。 XML 被设计用来传…

Killing LeetCode [83] 删除排序链表中的重复元素

Description 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 Intro Ref Link:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/ Difficulty:Easy Tag&am…

全面讲解最小二乘法

常见的最小二乘法我们就不多说了,下面主要介绍一下最小二乘法的一些先进方法。 正则化的最小二乘法 在使用常见的最小二乘法进行回归分析时,常常会遇到过拟合的问题,也就是在训练数据集上表现的很好,但是在测试数据集上表现的很…

Python 实现语音转文本

Python 实现语音转文本 Python可以使用多种方式来实现语音转文本,下面介绍其中两种。 方法一:使用Google Speech API Google Speech API 是 Google 在 2012 年推出的一个 API,可以用于实现语音转文本。使用 Google Speech API 需要安装 Sp…

64位交叉编译器版本切换

操作系统版本 lkmaoubuntu:~$ cat /proc/version Linux version 5.6.18 (lkmaoubuntu) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #1 SMP Tue Jul 25 23:11:17 PDT 2023 lkmaoubuntu:~$ 64位 安装默认版本和gcc5版本。 sudo apt-get install gcc-aarch64-linux-g…

无涯教程-Perl - if...elsif...else语句函数

if 语句后可以跟可选的 elsif ... else 语句,这对于使用单个if ... elsif语句测试各种条件非常有用。 if...elsif...else - 语法 Perl编程语言中的 if ... elsif...else语句的语法是- if(boolean_expression 1) {# Executes when the boolean expression 1 is tr…

C语言案例 打印秋水仙花数-05

题目:打印出所有的水仙花数。 步骤一:定义程序目标 编写一个C程序,输出所有的水仙花数。步骤二:程序设计 原理:所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身例如:153是一个“…

JavaScript 一段代码快速入门

仅记录了和c有所不同之处,其余类似。 一段简单代码 // 关注点分离,指html页面设计和javascript页面行为分离// 对象,键值对形式 const user {name: "gyf",age: 20,jobs: ["front-end", "engineer", 2, true…

智能卡通用安全检测指南 思度文库

范围 本标准规定了智能卡类产品进行安全性检测的一般性过程和方法。 本标准适用于智能卡安全性检测评估和认证。 规范性引用文件 下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅注日期的版本适用于本文件。凡是不注日期的引用文件,…

STM32 CubeMX USB_CDC(USB_转串口)

STM32 CubeMX STM32 CubeMX 定时器(普通模式和PWM模式) STM32 CubeMX一、STM32 CubeMX 设置USB时钟设置USB使能UBS功能选择 二、代码部分添加代码实验效果 ![请添加图片描述](https://img-blog.csdnimg.cn/a7333bba478441ab950a66fc63f204fb.png)printf发…

Python自动化实战之使用Pytest进行API测试详解

概要 每次手动测试API都需要重复输入相同的数据,而且还需要跑多个测试用例,十分繁琐和无聊。那么,有没有一种方法可以让你更高效地测试API呢?Pytest自动化测试!今天,小编将向你介绍如何使用Pytest进行API自…

【二】SPI IP核的使用

【一】SPI IP核使用:传送门 基于qsys通过spi外部总线协议对sd卡进行读写操作 一、实验平台与实验的目的: ​ 正点原子开拓者、芯片型号:EP4CE10F17C8;还需要一张sd卡。 ​ 该实验主要是利用SPI IP核驱动SD卡来实现读写实验&am…

05 并发编程挑战,上下文切换问题,死锁问题,受限于硬件和软件的资源问题

JAVA多线程 并发编程上下文切换如何减少上下文切换减少上下文切换实战 死锁资源限制的挑战什么是资源限制资源限制引发的问题如何解决资源限制的问题在资源限制情况下进行并发编程 并发编程 有的并发程序写得不严谨,在并发下如果出现问题,定位起来会比较…

Java中String方法学习总结_kaic

String详解 Java中的String是一个不可变的字符序列,它是Java中最常用的数据类型之一。 常用构造方法 String():无参构造方法,创建一个空字符串对象。 String(char[] value):通过字符数组创建一个字符串对象。 String(char[] val…

一文学透设计模式

设计模式是什么? 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,代表了解决一些问题的最佳实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 说白了,设计模式对于软件开发人员来说就…

K8S系列文章 之 容器网络基础 Docker0

什么是Docker0 使用ip addr命令看一下网卡&#xff1a; rootKitDevVps:~# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host…

优维低代码实践:对接数据

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

数据结构--单链表OJ题

上文回顾---单链表 这章将来做一些链表的相关题目。 目录 1.移除链表元素 2.反转链表 3.链表的中间结点 4.链表中的倒数第k个结点 5.合并两个有序链表 6.链表分割 7.链表的回文结构 8.相交链表 9.环形链表 ​编辑 10.环形链表II ​编辑 ​编辑 1.移除链表元素 思…