一文搞懂MySsql的Buffer Pool

Buffer Pool是什么

Buffer Pool是MySQL数据库中一个非常关键的组件。数据库中的数据最终都是存放在磁盘文件上的。但是在对数据库执行增删改查操作时,不可能直接更新磁盘上的数据。因为如果直接对磁盘进行随机读写操作,那速度是相当的慢的。随便一个大磁盘文件的随机读写操作,可能都要几百毫秒,这样数据库每秒也就只能处理几百个请求。

数据库执行增删改操作时,是基于内存Buffer Pool中的数据进行的。同时为了防止在更新完内存中的数据之后,由于机器宕机而造成数据丢失,数据库引入了redo日志机制,即增删改时会把修改也写入redo日志中。

Buffer Pool就是数据库的一个内存组件,里面缓存了磁盘上的真实数据。当执行更新时,会写undo日志、修改Buffer Pool数据、写redo日志;当提交事务时,会将redo日志刷磁、binlog刷盘、添加commit标记。最后后台IO线程会随机把Buffer Pool里的脏数据刷入到磁盘数据文件中。 

为什么要有 Buffer Pool?

虽然说 MySQL 的数据是存储在磁盘里的,但是也不能每次都从磁盘里面读取数据,这样性能是极差的。

要想提升查询性能,加个缓存就行了嘛。所以,当数据从磁盘中取出后,缓存内存中,下次查询同样的数据的时候,直接从内存中读取。

为此,Innodb 存储引擎设计了一个缓冲池(Buffer Pool,来提高数据库的读写性能。

有了缓冲池后:

  • 当读取数据时,如果数据存在于  Buffer Pool 中,客户端就会直接读取  Buffer Pool 中的数据,否则再去磁盘中读取。

  • 当修改数据时,首先是修改  Buffer Pool  中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。

如何配置Buffer Pool的大小 

由于Buffer Pool本质就是数据库的一个内存组件,所以Buffer Pool是有大小的,不能无限大。

Buffer Pool的默认大小是128MB,有点偏小。在实际生产环境下可以对Buffer Pool进行调整。比如对于16核32GB的数据库,可以给Buffer Pool分配2GB大小的内存。

[server]innodb_buffer_pool_size = 2147483648

 Buffer Pool 缓存什么?

InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool  同样需要按「页」来划分。

在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。

所以,MySQL 刚启动的时候,你会观察到使用的虚拟内存空间很大,而使用到的物理内存空间却很小,这是因为只有这些虚拟内存被访问后,操作系统才会触发缺页中断,接着将虚拟地址和物理地址建立映射关系。

Buffer Pool  除了缓存「索引页」和「数据页」,还包括了 undo 页,插入缓存、自适应哈希索引、锁信息等等。

为了更好的管理这些在 Buffer Pool 中的缓存页,InnoDB 为每一个缓存页都创建了一个控制块,控制块信息包括「缓存页的表空间、页号、缓存页地址、链表节点」等等。

控制块也是占有内存空间的,它是放在 Buffer Pool 的最前面,接着才是缓存页,如下图:

上图中控制块和缓存页之间灰色部分称为碎片空间。

为什么会有碎片空间呢?

 

你想想啊,每一个控制块都对应一个缓存页,那在分配足够多的控制块和缓存页后,可能剩余的那点儿空间不够一对控制块和缓存页的大小,自然就用不到喽,这个用不到的那点儿内存空间就被称为碎片了。

当然,如果你把 Buffer Pool 的大小设置的刚刚好的话,也可能不会产生碎片。

查询一条记录,就只需要缓冲一条记录吗?

不是的。

当我们查询一条记录时,InnoDB 是会把整个页的数据加载到 Buffer Pool 中,因为,通过索引只能定位到磁盘中的页,而不能定位到页中的一条记录。将页加载到 Buffer Pool 后,再通过页里的页目录去定位到某条具体的记录。

free链表可判断哪些缓存页是空闲的

当数据库运行起来后,肯定会不停地进行增删改查操作。此时会从磁盘上读取一个个的数据页放入到Buffer Pool中的缓存页里。

默认情况下,磁盘上的数据页和缓存页是一一对应的,都是16KB。Buffer Pool把数据缓存起来后,就可以对数据在内存里执行增删改查。

但是当数据库从磁盘上读取数据页放入Buffer Pool中的缓存页时,首先需要解决一个问题:哪些缓存页是空闲的?

为此,数据库为Buffer Pool设计了一个free链表,它是一个双向链表。在这个free链表里,每个节点就是一个空闲缓存页的描述数据块的地址。只要一个缓存页是空闲的,则其描述数据块的地址就会被放入free链表中。所以数据库刚启动时,如果此时所有的缓存页都是空闲的,那么所有缓存页的描述数据块就会被放进该free链表里。

简单LRU链表的工作原理

假设InnoDB从磁盘加载一个数据页到缓存页时,就把这个缓存页的描述数据块放到LRU链表头部去。

那么只要一个缓存页有数据,那么该缓存页就会在LRU里。并且最新加载数据的缓存页,会被放到LRU链表的头部。

假设某个缓存页的描述数据块本来在LRU链表的尾部,后面只要查询或者修改了这个缓存页的数据,也会把其描述数据块挪动到LRU链表头部。

总之,就是保证最近被访问过的缓存页,一定在LRU链表的头部。这样当缓冲区没有空闲的缓存页时,可以在LRU链表尾部找一个缓存页。而这个缓存页就是最近最少被访问的那个缓存页。然后把LRU链表尾部的那个缓存页刷入磁盘从而腾出一个空闲的缓存页,最后把需要的磁盘数据页加载到这个空闲的缓存页中即可。

这个LRU链表需要一定长度,不能只有2个节点。否则如果先是节点1被访问100次,接着到节点2被访问。这样虽然链表尾部是节点1,但实际上节点1是最近最少被访问的。

简单LRU链表可能存在的预读问题

在LRU链表的尾部,一定是最近最少被访问的那个缓存页。但这个LRU机制在实际运行中,面对MySQL的预读机制,会有问题。

MySQL预读,指的是从磁盘加载一个数据页时,可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里。比如现在有两个空闲缓存页,在加载一个数据页时,就会连带着把其相邻的一个数据页也加载到缓存里去。但是接下来只有一个缓存页被访问了,另外一个通过预读机制加载的缓存页,其实并没被访问,而此时这两个缓存页可能都在LRU链表前面。

触发MySQL预读机制的情况

情况一:参数innodb_read_ahead_threshold默认值是56,意思是如果顺序访问一个区的多个数据页的数量超过了该阀值。就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去。

情况二:Buffer Pool里缓存一个区13个连续的会被频繁访问的数据页,此时就会直接触发预读机制,把这个区里的其他数据页也加载到缓存里。该情况通过参数innodb_random_read_ahead控制,默认OFF表示关闭。

所以,默认情况下第一种情况很可能会触发预读机制。并且第一种情况会一下子把相邻区中很多数据页加载到缓存里。这些缓存页如果都放在LRU链表前面,并且没什么访问了。这样就会导致一些频繁被访问的缓存页放到了LRU链表的尾部。最后造成频繁被访问的缓存页反而被清空掉。而被清空掉的缓存页很快又要从磁盘中重新加载进入缓冲区。这时不但不合理还很影响性能。

简单LRU链表可能存在的全表扫描问题

全表扫描,就是类似于执行这样的SQL语句:select * from users。此时没有加任何一个where条件,这个会导致MySQL把该表所有的数据页,都从磁盘加载到Buffer Pool里。

这时LRU链表中排在前面的缓存页,可能都是全表扫描加载进来的缓存页。而如果这次全表扫描后,后面几乎没有用到这个表里的数据。那此时LRU链表的尾部,也可能都是之前一直被频繁访问的缓存页。这样也会把频繁访问的缓存页给淘汰掉,最后留下不经常访问的全表扫描加载进来的缓存页。

总结

所以如果使用简单的LRU链表机制,其实是漏洞百出的。因为预读机制、全表扫描会把未来并不经常访问的数据页加载到缓存页里,从而导致那些频繁被访问的缓存页不得不处于LRU链表尾部。如果此时恰好需要把一些缓存页刷入磁盘或者清空以腾出空闲的缓存页,那么就会把频繁被访问的缓存页给清空了。

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

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

相关文章

软考(高项)系统分析师--论文写作技巧

文章目录 前言一、论文的结构和要求:1.1 论文的结构:1.2 论文的要求: 二、论文每段的写法:2.1 解题:2.2 摘要:2.2.1 第一段:2.2.2 第二段: 2.3 正文:2.3.1 项目背景&…

TikTok美区日销二十万美金爆款黑马!胸贴赛道成功起飞!

从去年开始,一项名为“No bra”(无胸罩)的挑战就长期刷屏TikTok。随着平台内各大博主和明星站台发声,越来越多用户也参与其中,话题的热度逐渐走向高潮。截止到目前, TikTok上相关话题累计播放量已高达8.3亿…

Eureka从入门到精通面试题及答案参考

什么是Eureka?它在微服务架构中扮演什么角色? Eureka是Netflix开源的一款基于REST的服务发现组件,它主要应用于构建分布式系统中的服务注册与发现。在微服务架构中,Eureka扮演着至关重要的角色,它让微服务架构中的各个服务实例能够互相发现、相互调用,从而实现了服务之间…

ubuntu 22.04配置国内镜像源-2024.7月最新版

Ubuntu 22.04 LTS这是一个长期支持版本,它将被支持五年,直到2027年4月。已发布的LTS版本带来了一些新的功能 国内有很多Ubuntu 22.04的镜像源,包括阿里、网易,还有很多教育网的镜像源,比如清华源、中科大源。 在教程中…

django admin添加自己的页面

建立模型 如果要单独建一个页面,用于展示model的数据,可以新建一个model,继承自要展示的那个类 class ViewsByDayModel(ViewsByDay): # 父类为要展示的model类class Meta:proxy True # 使用代理verbose_name 每日浏览次数统计verbose_nam…

仿Antd-mobile的Cascader实现省市区联动

为啥不直接用Cascader 级联选择组件呢?主要是因为作为老项目,已经引入了antd-mobile2.3.4,同时引入v5版本会有兼容性问题。 原始数据格式: 首先需要将后端返回的数据转为前端定义的格式,方便使用: [{&qu…

第二十六章 生成器(generator)(Python)

文章目录 前言一、生成器函数 前言 在 Python 中,使用了 yield 的函数被称为生成器(generator) yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值&#xff…

jsqlparse工具拦截sql处理和拓展

前置知识 访问者模式 (Visitor Pattern)是一种行为设计模式,它允许你定义在不改变被访问元素的类的前提下,扩展其功能。通过将操作(操作或算法)从对象结构中提取出来,可以在不修改这些对象的前…

网络扫描工具Nmap,用于发现网络中的主机和服务

Nmap 是一个非常强大的网络扫描工具,可以用于发现网络中的主机和服务。使用 Nmap 可以获取关于目标系统的各种信息,包括开放端口、操作系统版本、服务版本等。以下是如何使用 Nmap 以及一些常见的用例: 安装 Nmap 在 Ubuntu 22.04 上安装 N…

机器学习与模式识别_清华大学出版社

contents 前言第1章 绪论1.1 引言1.2 基本术语1.3 假设空间1.4 归纳偏好1.5 发展历程1.6 应用现状 第2章 模型评估与选择2.1 经验误差与过拟合2.2 评估方法2.3 性能度量2.3.1 回归任务2.3.2 分类任务 2.4 比较检验2.5 偏差与方差2.5.1 偏差-方差分解2.5.2 偏差-方差窘境 第3章 …

航空数据管控系统-②项目分析与设计:任务2:使用Git或SVN管理项目(可选任务,只介绍Git安装)

任务描述 1、安装Git 2、注册GitHub 3、配置本地库 4、配置远程库 5、使用Git管理项目 任务指导 分为以下几个部分完成: 学会Git的安装,帐号注册本地存储库的管理自己创建一个项目,项目名称为自己的名字,上传到代码仓库&#xff…

校验是否是合法日期字符串正则

常用的ui组件不支持日月的这种形式&#xff0c;产品提成要用input框的形式来做&#xff0c;且对输入字符串是否符合时间格式做校验&#xff0c;写起来还是很麻烦的&#xff0c;记录一下以后备用。 <script> /*** 校验是否是合法日期字符串* 支持格式&#xff1a;* …

符号速率、调制方式、码率、传输速率

符号速率、调制方式、码率&#xff08;比特率&#xff09;和传输速率在通信系统中是相互关联且重要的概念&#xff0c;它们之间的关系可以归纳如下&#xff1a; 一、定义 符号速率&#xff08;Symbol Rate&#xff09;&#xff1a; 也称波特率&#xff08;Baud Rate&#xff…

C++ STL容器:序列式容器-队queue,deque

摘要&#xff1a; CC STL&#xff08;Standard Template Library&#xff0c;标准模板库&#xff09;在C编程中的重要性不容忽视&#xff0c;STL提供了一系列容器、迭代器、算法和函数对象&#xff0c;这些组件极大地提高了C程序的开发效率和代码质量。 STL 容器 分为 2 大类 …

Oracle数据库中导出多个表

要在Oracle数据库中导出多个表&#xff0c;可以使用exp&#xff08;Export&#xff09;工具或expdp&#xff08;Data Pump Export&#xff09;工具。下面是两种方法的详细说明&#xff1a; 1. 使用exp工具导出多个表&#xff1a; exp是Oracle的传统导出工具&#xff0c;它允许…

一文搞懂到底什么是 AQS

日常开发中&#xff0c;我们经常使用锁或者其他同步器来控制并发&#xff0c;那么它们的基础框架是什么呢&#xff1f;如何实现的同步功能呢&#xff1f;本文将详细讲解构建锁和同步器的基础框架--AQS&#xff0c;并根据源码分析其原理。 一、什么是 AQS&#xff1f; (一) AQS…

树的笔记()

文章目录 菜单数据库设计什么结构便于使用 树结构是应用非常广泛的结构&#xff0c;甚至可以说无处不在。 电脑里的文件夹是不是树结构&#xff0c;省市区县镇乡村是不是树结构&#xff0c;菜单是不是树结构&#xff0c;军队是不是树结构&#xff0c;职位是不是树结构。。。太多…

ES module 工作原理浅谈

ES module&#xff08;ECMAScript module&#xff09;的工作过程涉及三个主要步骤&#xff1a;获取、解析和实例化。 获取步骤涉及获取所有需要的模块。当JavaScript引擎遇到import语句时&#xff0c;它首先查看module map是否已经有了一个与URL匹配的条目。如果没有&#xff…

Element中的日期时间选择器DateTimePicker和级联选择器Cascader

简述&#xff1a;在Element UI框架中&#xff0c;Cascader&#xff08;级联选择器&#xff09;和DateTimePicker&#xff08;日期时间选择器&#xff09;是两个非常实用且常用的组件&#xff0c;它们分别用于日期选择和多层级选择&#xff0c;提供了丰富的交互体验和便捷的数据…

CD4017 – 带解码输出的十进制计数器

CD4017 IC 是一个十进制计数器&#xff0c;它有 10 个输出&#xff0c;分别代表 0 到 9 的数字。计数器在&#xff08;14号引脚&#xff09;每个时钟脉冲上升时增加 1。计数器达到 9 后&#xff0c;它会在下一个时钟脉冲时从 0 重新开始。 引脚名称管脚 &#xff03;类型描述VD…