【MySQL自身的性能优化】InnoDB 的 Buffer Pool

这里写目录标题

  • 一、引入缓存的重要性
  • 二、InnoDB 的 Buffer Pool
    • 1. Buffer Pool 内部组成
    • 2. free 链表管理空闲页
    • 3. flush 链表管理脏页
    • 4. LRU 链表提高缓存命中
      • 那咱需要咋地解决预读问题呢?
      • 那咱需要咋地解决 Buffer Pool 污染问题呢?
    • 5. 脏页什么时候被刷入磁盘?
  • 三、总结

有近俩个来月没有写博客了,也不知道自己在瞎忙活什么。之前我有写过一篇关于 MySQL 的内存池一篇博客 InnoDB体系架构之内存池(buffer pool),那是看楠哥视频后总结出来的。然后现是看了《MySQL 是怎样运行的》一书,也可能是基础相比之前要好了些,理解的要好点了吧,就再总结一篇,现进入正文。

一、引入缓存的重要性

无论是系统数据还是用于存储用户数据的索引(包括聚簇索引和二级索引),都是以页为基本单位存放在表空间中。所谓的表空间,只不过是 InnoDB 对一个或几个实际文件的抽象(ibd)。说到底数据是存放在磁盘上的,那磁盘的速度是非常慢的,而且在磁盘缓冲区和内存之间进行交互的时候也是需要霸占着CPU的,不管是在性能上还是在资源上都是一种不好的选择。所以 InnoDB 存储引擎在处理客户端的请求时,如果需要访问某个页的数据,就会把完整的页中的数据全部加载到内存中。也就是说,即使你是需要访问某索引页的一条记录,也需要先把整个页的数据加载到内存中(这一方面是和MySQL存储数据是在索引中,另一方面就是为了更好的缓存数据)。将整个页加载到内存中后就可以进行读写访问了,而且在读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省下磁盘 I/O 的开销了。

二、InnoDB 的 Buffer Pool

为缓存上面提到的磁盘中的页,在 MySQL 服务器启动时就向操作系统申请了一片连续的内存,这块内存的名字就叫 Buffer Pool(缓冲池)
查看 innodb_buffer_pool_size 可以查看到其申请的内存大小,我这分配的是 128mb;
在这里插入图片描述

1. Buffer Pool 内部组成

整个Buffer Pool 由控制块、缓冲页和碎片组成。

  • 控制块:它记录着缓冲页的描述信息,比如该页所属的表空间编号、页号、缓冲页在 Buffer Pool 中的地址等等。设置的 innodb_buffer_pool_size 的大小不包含这个
  • 缓冲页:buffer pool 中存放的【数据页】称之为【缓冲页】,和磁盘上的数据页是一一对应的,都是16KB,缓冲页的数据,是从磁盘上加载到 buffer pool 当中的一个完整页。
  • 碎片:若设置的Buffer Pool 放满了缓冲页,剩余的内存则称之为碎片。

在这里插入图片描述

2. free 链表管理空闲页

在最初启动 MySQL 服务器的时候,需要完成 Buffer Pool 的初始化工作。先向系统中申请 Buffer Pool 的内存空间,然后把它划分成若干对控制块和缓冲页。此时是没有磁盘页被存到 Buffer Pool 中的。
而有个 free 链表就是用来管理这些空闲页的,以控制块作为节点,进行连接组成的一个链表
在这里插入图片描述当有磁盘页需要被缓存到 Buffer Pool 中时,就可以进行下面操作

  1. 可以取出一个空闲的缓冲页;
  2. 然后将其描述信息填入控制块;
  3. 最后将 free 链表中的对应节点删除,表示已经不再空闲,而哪个磁盘页就放到对应的缓冲页的位置上。
  4. 然后将 表空间号 + 页号 作为 key,控制块地址作为 value,存入到一个哈希表中,方便后续读取或写对应的缓冲页。

若读某页的逻辑就大概如下:
在这里插入图片描述

3. flush 链表管理脏页

设计 Buffer Pool 除了可以提高读性能,也可提高写性能。当需要更新数据的时,若可以根据 表空间号 + 页号 可以找到对应的控制块地址,说明 Buffer Pool 存在对应的缓冲页,那么进行写操作可以选择直接对 缓冲页 进行写操作,那么它就和对应磁盘上的数据不一致了,这样的缓冲页也被称为脏页

这些脏页的控制块最后也会被拼接成一个链表——flush 链表
在这里插入图片描述和 Free 链表是一样的,只是区别是 Free 链表的结点是空闲缓冲页的控制块,而 Flush 链表的结点是脏页的控制块。
有了 Flush 链表之后,后台线程就可以通过遍历这个链表,然后将脏页写入到磁盘

在这里插入图片描述从上图的InnoDB存储引擎的体系架构中看出其后台线程所担任的角色:
负责刷新内存池中的数据,保证缓存池中的内存缓存是最近的数据。此外将已修改的数据文件刷新到磁盘文件中,同时保证在数据库中发生异常的情况下 InnoDB 能恢复到正常运行状态。(如:Master Thread:负责将缓存池中的数据异步刷新到磁盘;IO Thread:异步处理IO请求,提高数据库的性能

4. LRU 链表提高缓存命中

看见 LRU(Least Recently Used) 第一反应就是最近最少使用淘汰策略
这里使用它的目的就是因为内存的针对,Buffer Pool 的内存资源也是有限的,当无法缓存新的数据的时候,希望把一些不用的缓冲页给淘汰掉,空闲出缓冲页供新的数据页使用。
针对上面的需求,Buffer Pool 使用了 LRU 淘汰策略去实现。

简单的 LRU 算法的实现思路是这样:

  • 当访问的页在 Buffer Pool 里,就直接把该页对应的 LRU 链表节点移动到链表的头部;
  • 当访问的页不在 Buffer Pool 里,除了要把页放入到 LRU 链表的头部,还要淘汰 LRU 链表末尾的节点。

阐述了这三种链表,咱就知道 Buffer Pool 是怎样管理数据的了:
在这里插入图片描述解释图:

  • Free Page(空闲页),表示此页未被使用,位于 Free 链表;
  • Clean Page(干净页),表示此页已经被使用,但是页面未发生修改,位于 LRU 链表;
  • Dirty Page(脏页),表示此页【已被使用】且【已经被修改】,其数据和磁盘上的数据已经不一致了。当脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该页就变成了干净页。脏页同时存在于 LRU 链表和 Flush 链表。

简单的 LRU 算法并没有被 MySQL 使用,因为简单的 LRU 算法无法避免下面这俩个问题:

  • 预读失效;
  • Buffer Pool 污染;

何为预读?
Innodb 提供了一个看起来比较贴心的服务——预读。所谓预读,就是 InnoDB 认为执行当前的请求时,可能会在后面读取某些页面,于是就预先把这些页面加载到 Buffer Pool 中。默认是线性预读,而不是随机预读。

咱就是说预读本来是个好事,但是如果预读的页用不到呢?插入到 LRU 链表的头部,然后满了的话把尾部的页都淘汰掉,从而大大降低 Buffer Pool 的命中率。

大部分情况下局部性原理还是可靠的。

那咱需要咋地解决预读问题呢?

想要避免这种预读效果带来的影响,最好就是让预读页停留在 Buffer Pool 里的时间尽可能的短,让真正被访问的页才移动到 LRU 链表的头部,从而保证真正被读取的热数据留在 Buffer Pool 里的时间尽可能长

MySQL 改进了 LRU 算法,将 LRU 划分了俩个区域:Old 区域 和 Young 区域

在这里插入图片描述Young区域和Old区域在 LRU 链表的占比是由 innodb_old_blocks_pct 参数设置的。

划分这俩个区域后,预读的页就只需要加入到 Old 区域的头部,当页被真正访问的时候,才将页插入 Young 区域的头部。如果预读的页一致没有被访问,就会从 Old 区域移除,这样就不会影响 Young 区域的热点数据了。

假设有一个长度为 10 的 LRU 链表,其中 young 区域占比 70 %,old 区域占比 30 %。
在这里插入图片描述

现在有个编号为 20 的页被预读了,这个页只会被插入到 old 区域头部,而 old 区域末尾的页(10号)会被淘汰掉。

在这里插入图片描述

如果 20 号页一直不会被访问,它也没有占用到 young 区域的位置,而且还会比 young 区域的数据更早被淘汰出去。

如果 20 号页被预读后,立刻被访问了,那么就会将它插入到 young 区域的头部,young 区域末尾的页(7号),会被挤到 old 区域,作为 old 区域的头部,这个过程并不会有页被淘汰。

在这里插入图片描述

虽然通过划分 old 区域 和 young 区域避免了预读失效带来的影响,但是还有个问题无法解决,那就是 Buffer Pool 污染的问题。

何为 Buffer Pool 污染?
当某个 SQL 语句扫描了大量的数据时,在 Buffer Pool 空间比较有限的情况下,可能会将 Buffer Pool 里的所有页都替换出去,导致大量数据被淘汰了,等这些热数据又被再次访问的时候,由于缓存未能命中,就会产生大量的磁盘 IO,MySQL 性能就会急剧下降,这个过程被称为 Buffer Pool 污染
只要咱说无用查询,这种LRU策略就会引起 Buffer Pool 污染。

比如数据量很大的表 t_user,执行了下面这个查询:

select * from t_user where name like "%xiaolin%";

即时这查询的结果就几条,但是这也会引起索引失效,这样就得进行全表扫描(就是说从聚簇索引的叶子节点记录、节点遍历),接下来会发生下面这些过程:

  • 从磁盘读到的页加入到 LRU 链表的 Old 区域头部;
  • 当从页里读取行记录时,也就是页被访问的时候,就要将该页放到 Young 区域头部;
  • 接下来拿行记录的 name 字段和字符串 xiaolin 进行模糊匹配,如果符合条件,就加入到结果集里;
  • 如此往复,直到扫描完表中的所有记录。

经过这一番折腾,原本 Young 区域的热点数据都会被替换掉。

举个例子,假设需要批量扫描:21,22,23,24,25 这五个页,这些页都会被逐一访问(读取页里的记录)。
在这里插入图片描述

在批量访问这些数据的时候,会被逐一插入到 young 区域头部。

在这里插入图片描述

可以看到,原本在 young 区域的热点数据 6 和 7 号页都被淘汰了,这就是 Buffer Pool 污染的问题。

那咱需要咋地解决 Buffer Pool 污染问题呢?

很多缓存页只是被访问了一次,但是却只因为被访问了一次而进入到 Young 区域,从而导致热点数据被替换了。现在我们只要提高 Young 区域的门槛,这样就可以有效地保证 Young 区域里的热点数据不会被替换掉。

MySQL 是这样做的,进入到 Young 区域条件增加了一个停留在 Old 区域的时间判断。

具体是这样做的,在对某个处在 Old 区域的缓存页进行第一次访问时,就在它对应的控制块中记录下来这个访问时间:

  • 如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该缓存页就不会被从 old 区域移动到 Young 区域的头部;
  • 如果后续的访问时间与第一次访问的时间不在某个时间间隔内,那么该缓存页移动到 young 区域的头部;

这个间隔时间是由 innodb_old_blocks_time 控制的,默认是 1000 ms。

就是说,只有同时满足【被访问】与【在 Old 区域停留时间超过 1 秒】俩个条件,才会被插入到 Young 区域头部,这样就解决了 Buffer Pool 污染问题。

另外,MySQL 针对 young 区域其实做了一个优化,为了防止 young 区域节点频繁移动到头部。young 区域前面 1/4 被访问不会移动到链表头部,只有后面的 3/4被访问了才会。

5. 脏页什么时候被刷入磁盘?

若修改数据时 Buffer Pool 有这个数据,那么自然会直接修改 BufferPool 控制块映射的缓存页,这样一来这个缓存页就被称为脏页,其控制块也会被放入到 Flush 链表中进行管理。

但是脏页终究还是要被刷入磁盘的,保证缓存和磁盘数据的一致,一般情况下为了不影响性能,都会在一定时机进行批量刷盘。

可能大伙担心,如果脏页还没有来得及刷入到磁盘内,MySQL 宕机了,不就丢失数据了吗?
这个不用担心,InnoDB 考虑到这一点,更新操作采用的是 Write Ahead Log 策略,就是说先写入日志,再写入磁盘,通过 redo log 日志让 MySQL 拥有了崩溃恢复的能力

下面几种情况会触发脏页的刷新:

  • 当 redo log 日志满了的情况下,会主动触发脏页刷新到磁盘;
  • Buffer Pool 空间不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
  • MySQL 认为空闲时,后台线程会定期将适量的脏页刷入到磁盘;
  • MySQL 正常关闭之前,会把所有的脏页刷入到磁盘;

在开启慢 SQL 监控后,如果你发现 【偶尔】会出现用时稍长的 SQL ,这可能是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

如果间断地出现这种现象,就需要调大 Buffer Pool 空间或 Redo Log 日志的大小。

三、总结

为尽可能地降低磁盘 IO,来提高数据库的读写性能,InnoDB 存储引擎设计了一个 缓冲池(Buffer Pool)

Buffer Pool 以页为单位缓冲数据,可以通过 innodb_buffer_pool_size 参数调整缓冲池的大小,默认是 128 M。

Buffer Pool 的内部组成有三种角色,控制块、缓冲页、碎片

InnoDB 通过三种链表来管理缓冲页:

  • Free List(空闲页链表),管理空闲页;
  • Flush List(脏页链表),管理脏页;
  • LRU List(淘汰链表),管理脏页+干净页,将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。

InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,而 InnoDB 做了以下俩点优化(当然还有很多优化点,这里阐述的是重要的俩个):

  • 将 LRU 链表分为了 Young 和 Old 区域俩个部分加入缓冲池的页优先被放到 Old 区域,页被访问时,才进入到 Young 区域,目的是为了解决预读失效问题
  • 当 【页被访问】 和 【Old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)】 时,才会将页插入到 Young 区域,否则还是插入到 Old 区域,目的是为了解决批量数据访问(如全表扫描),大量热数据淘汰的问题。

可以通过调整 innodb_old_blocks_pct 参数,设置 young 区域和 old 区域比例。

在开启了慢 SQL 监控后,如果你发现 【偶尔】 会出现一些用时稍长的 SQL,这是因为脏页在刷新到磁盘时导致数据库性能抖动。如果在很短的时间出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

参考文献:

  1. 《MySQL是怎样运行的》第十七章 InnoDB 的 Buffer Pool。
  2. 小林的解开Buffer Pool 的面纱

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

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

相关文章

pyqt5+python子域名扫描程序

import sysfrom PyQt5 import uic from PyQt5.QtWidgets import * #requests库内置了不同的方法来发送不同类型的http请求 import requests#BS主要功能是从网页抓取数据,提供一些简单的、python 式的函数用来处理导航、搜索、修改分析树等功能 from bs4 import Beau…

WebSocket协议、与HTTP对比

WebSocket 也可前往本人的个人网站进行阅读 WebSocket 和 HTTP WebSocket和HTTP协议一样,都是基于TCP协议实现的应用层协议。 HTTP协议通常是单边通信,主要用于传输静态文档、请求-响应通信,适用于Web浏览器加载网页、API调用等。然而Web…

NX二次开发获取圆弧的四个象限点

我是用来用来画水路线框的UF_MODL_ask_curve_points()可以按弧长或者弧度获取曲线的等分点,取PI/2的圆弧,即将圆弧四等分,你也可以取任意等分点。 int GetArcPoint(tag_t arc_tag,double point[4][3]) {if(arc_tag0)r…

KubeSphere 核心实战之二【在kubesphere平台上部署redis】(实操篇 2/4)

文章目录 1、登录kubesphere平台2、redis部署分析3、redis容器启动代码4、kubesphere平台部署redis4.1、创建redis配置集4.2、创建redis工作负载4.3、创建redis服务 5、测试连接redis 在kubesphere平台上部署redis应用都是基于redis镜像进行部署的,所以所有的部署操…

DRmare Music Converter - 一款高效的音乐转换工具,让您的音乐无处不在!

DRmare Music Converter是一款专业的音乐转换工具,旨在帮助用户更方便地管理和享受音乐。无论您是使用Mac还是Windows操作系统,DRmare Music Converter都能为您提供高效、便捷的音乐转换体验。 DRmare Music Converter支持多种音频格式的转换&#xff0…

伊恩·斯图尔特《改变世界的17个方程》波动方程笔记

主要是课堂的补充(yysy,我觉得课堂的教育模式真有够无聊的,PPT、写作业、考试,感受不到知识的魅力。 它告诉我们什么? 小提琴琴弦上某个小段的加速度,与相邻段相对于该段的平均位移成正比。 为什么重要&…

Studio One2024免费版下载及入门教程分享

众所周知,Studio One是一个专业的音频编辑软件,近几年随着音视频剪辑越来越火,Studio One也逐渐被人们所熟知。最近,就有许多小伙伴私信我,寻求Studio One的入门教程。 这不,今天小编就给大家带来了音频剪…

一个好用的工具,对网工来说是绝杀技!

上午好,我是老杨。 提到用人,很多单位和管理者第一反应都是应聘者的能力。能力到底怎么界定,其实每个人都有不同的判定标准。 在我看来,做事专注,且能尽可能“偷懒”的网工 ,就是我个人筛选员工的标准。 …

Python seaborn库的边框设置(Seaborn篇-02)

Python seaborn库的边框设置(Seaborn篇-02)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 …

RT-Thread Studio学习(十七)虚拟串口

RT-Thread Studio学习(十七)虚拟串口 一、简介二、新建RT-Thread项目并使用外部时钟三、启用USB设备功能四、测试 一、简介 本文将基于STM32F407VET芯片介绍如何在RT-Thread Studio开发环境下实现USB虚拟串口。 硬件及开发环境如下: OS WI…

AI学习(2): PyTorch2.x环境安装

1.介绍 注:下面是对PyTorch进行了简单的介绍,不喜欢可直接跳过。 1.1 什么是PyTorch PyTorch是一个由Facebook人工智能研究团队开发的开源机器学习库,用于开发人工智能和深度学习的应用程序。PyTorch支持广泛的机器学习和深度学习算法,并基于…

Flink Kubernetes Operator 介绍

一、简介 Flink Kubernetes Operator是针对在Kubernetes上运行Apache Flink应用程序而设计的工具。它充分利用了Kubernetes的优势,实现了对Flink集群的弹性管理和自动化操作,通过扩展Kubernetes API的方式,提供了管理和操作Flink部署的功能。…

探索设计模式的魅力:一篇文章让你彻底搞懂建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,旨在将一个复杂对象的创建过程与其表示分离,使得同样的构建过程可以创建不同的表示形式。 主要角色: 产品(Product):表示正在构建…

git提权

实验环境——vulnhub-dc2靶场 git提权 前提:用户可以使用sudo中git权限 查看sudo权限 sudo -l可以发现git命令存在sudo提权 基于此进行权限提升 方式: sudo git help config #在末行命令模式输入 !/bin/bash 或 !sh #完成提权 sudo git -p help…

海外问卷调查怎么做?

大家好,我是橙河老师,我自己做海外问卷项目已经2年时间了,一般来说互联网项目的生命周期都不会太长,但海外问卷项目是一个稳定长期可做的项目,只要消费市场一直存在,问卷调查的需求就不会消失,我…

DBA技术栈MongoDB:简介

1.1 什么是MongoDB? MongoDB是一个可扩展、开源、表结构自由、用C语言编写且面向文档的数据库,旨在为Web应用程序提供高性能、高可用性且易扩展的数据存储解决方案。 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当…

深度学习记录--偏差/方差(bias/variance)

误差问题 拟合神经网络函数过程中会出现两种误差:偏差(bias)和方差(variance) 偏差和误差的区别 欠拟合(underfitting) 当偏差(bias)过大时,如左图,拟合图像存在部分不符合值,称为欠拟合(underfitting) 过拟合(overfitting) …

gitlab 部署项目新分支

公司代码管理平台新切换到gitlab下,上线发版流程随之变更 1新建分支,开发完成,提交新分支 2.去gitlab平台上找到Merge requests 3 点击右上角的New merge request select source branch 选择新建的分支 点击 compare branches and contin…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-热门标签推荐显示实现

锋哥原创的SpringbootLayui python222网站实战: python222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火…