当前位置: 首页 > news >正文

图解mysql(六)——内存篇

6 揭开Buffer Pool的面纱

image-20220922211452827

6.1 为什么要有Buffer Pool?

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

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

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

6.1.1 Buffer Pool有多大

image-20220922215520447

Buffer Pool 是在 MySQL 启动的时候,向操作系统申请的一片连续的内存空间,默认配置下 Buffer Pool 只有 128MB

可以通过调整 innodb_buffer_pool_size 参数来设置 Buffer Pool 的大小,一般建议设置成可用物理内存的 60%~80%。

6.1.2 Buffer Pool缓存什么

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

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

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

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

6.2 如何管理Buffer Pool

6.2.1 如何管理空闲页

Buffer Pool 是一片连续的内存空间,当 MySQL 运行一段时间后,这片连续的内存空间中的缓存页既有空闲的,也有被使用的。

那当我们从磁盘读取数据的时候,总不能通过遍历这一片连续的内存空间来找到空闲的缓存页吧,这样效率太低了。

所以,为了能够快速找到空闲的缓存页,可以使用链表结构,将空闲缓存页的「控制块」作为链表的节点,这个链表称为 Free 链表(空闲链表)。

6.2.2 如何管理脏页

设计 Buffer Pool 除了能提高读性能,还能提高写性能,也就是更新数据的时候,不需要每次都要写入磁盘,而是将 Buffer Pool 对应的缓存页标记为脏页,然后再由后台线程将脏页写入到磁盘。

那为了能快速知道哪些缓存页是脏的,于是就设计出 Flush 链表,它跟 Free 链表类似的,链表的节点也是控制块,区别在于 Flush 链表的元素都是脏页。

6.2.3 如何提高缓存命中率

Buffer Pool 的大小是有限的,对于一些频繁访问的数据我们希望可以一直留在 Buffer Pool 中,而一些很少访问的数据希望可以在某些时机可以淘汰掉,从而保证 Buffer Pool 不会因为满了而导致无法再缓存新的数据,同时还能保证常用数据留在 Buffer Pool 中。

要实现这个,最容易想到的就是 LRU(Least recently used)算法。

该算法的思路是,链表头部的节点是最近使用的,而链表末尾的节点是最久没被使用的。那么,当空间不够了,就淘汰最久没被使用的节点,从而腾出空间。

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

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

6.2.4 脏页什么时候会被刷入磁盘

引入了 Buffer Pool 后,当修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,但是磁盘中还是原数据。

因此,脏页需要被刷入磁盘,保证缓存和磁盘数据一致,但是若每次修改数据都刷入磁盘,则性能会很差,因此一般都会在一定时机进行批量刷盘。

可能大家担心,如果在脏页还没有来得及刷入到磁盘时,MySQL 宕机了,不就丢失数据了吗?

这个不用担心,InnoDB 的更新操作采用的是 Write Ahead Log 策略,即先写日志,再写入磁盘,通过 redo log 日志让 MySQL 拥有了崩溃恢复能力。

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

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

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

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

6.3 总结

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

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

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

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

InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,而 InnoDB 做 2 点优化:

  • 将 LRU 链表 分为young 和 old 两个区域,加入缓冲池的页,优先插入 old 区域;页被访问时,才进入 young 区域,目的是为了解决预读失效的问题。
  • 当**「页被访问」且「 old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)」**时,才会将页插入到 young 区域,否则还是插入到 old 区域,目的是为了解决批量数据访问,大量热数据淘汰的问题。

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

是插入到 old 区域,目的是为了解决批量数据访问,大量热数据淘汰的问题。

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

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

相关文章:

  • 【数据库原理 | MySQL】 前世今生(入坑篇)
  • Python脚本在win10下开机自启动
  • docker安装rocketmq
  • 【C++】基础入门(三):引用超全整理
  • 【java_wxid项目】【第十四章】【Spring Cloud Stream集成】
  • python-pyecharts基础知识
  • 分类:概率生成模型 - 李宏毅机器学习笔记
  • Tkinter教程(每天半小时,3天彻底掌握Tkinter)day1
  • jsonp原理-node篇
  • 【C++初阶-类和对象-上】面向对象大法好
  • SpringBoot的starter到底是什么?
  • C++内存管理(1)- new和delete
  • Pytorch深度学习——实现手写数字识别 06(未完)
  • 【java中的反射】2.反射的应用
  • 机器学习GPU环境配置
  • 【译】JS基础算法脚本:字符串结尾
  • 【React系列】如何构建React应用程序
  • Angular4 模板式表单用法以及验证
  • CSS中外联样式表代表的含义
  • express + mock 让前后台并行开发
  • Js基础知识(一) - 变量
  • mysql外键的使用
  • Service Worker
  • Shell编程
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 阿里研究院入选中国企业智库系统影响力榜
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 来,膜拜下android roadmap,强大的执行力
  • 首页查询功能的一次实现过程
  • 我的zsh配置, 2019最新方案
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 因为阿里,他们成了“杭漂”
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 用jquery写贪吃蛇
  • 06-01 点餐小程序前台界面搭建
  • 【云吞铺子】性能抖动剖析(二)
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • ​虚拟化系列介绍(十)
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #《AI中文版》V3 第 1 章 概述
  • #100天计划# 2013年9月29日
  • #define 用法
  • #大学#套接字
  • $L^p$ 调和函数恒为零
  • %check_box% in rails :coditions={:has_many , :through}
  • (9)STL算法之逆转旋转
  • (C#)获取字符编码的类
  • (二)斐波那契Fabonacci函数
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (五)c52学习之旅-静态数码管
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • **python多态
  • *上位机的定义