本文大纲

Buffer Pool 基础

缓冲池 Buffer Pool 的作用

InnoDB 存储引擎是基于磁盘存储的。以页为单位存储数据。我们进行的增删改查操作本质上都是在操作数据页(包括读页、写页、创建新页)。由于CPU速度和磁盘速度之间的鸿沟,基于磁盘的数据库通常使用缓冲池来提高数据库的整体性能。

Buffer Pool 就是把磁盘上的页,缓存到内存中,用于降低与磁盘直接进行IO的成本。

InnoDB 引擎在处理客户端请求时,当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中。之后就可以对页进行读写访问了。操作完页之后并不会立即释放掉其内存空间,而是将其缓存起来,将来有请求再次访问该页数据时,就可以省去磁盘IO的开销了。

Buffer Pool 缓存什么?

MySQL启动时, InnoDB 引擎向操作系统申请一块连续的内存空间,然后按照页的大小(默认16KB)划分出一个个空页,当磁盘上的页缓存到内存的 Buffer Pool 中会对空页进行填充。

缓冲池中的数据页类型有:数据页、索引页、插入缓冲(insert buffer)、自适应哈希索引、锁信息、数据字典信息等。数据页和索引页是缓冲池中的主要内容。

查看设置缓冲池大小

InnoDB 引擎通过 innodb_buffer_pool_size 变量查看缓冲池的大小。一般建议设置成可用物理内存的 60%~80%

mysql > show variables like 'innodb_buffer_pool_size'

通过 set global innodb_buffer_pool_size 修改缓冲池大小。

缓冲池的 预读 特性

缓冲池的作用就是提升IO效率,而在读取数据时存在“局部性原理”。也就是说我们使用了一些数据,大概率还会使用它周围的一些数据。

当数据页从磁盘加载到 Buffer Pool 中时,会把相邻的数据页也加载到 Buffer Pool 中,使用 预读 机制提前加载出来,可以减少未来可能的磁盘IO。

管理 Buffer Pool

数据页管理

Buffer Pool 中的页有以下几种状态:

1、空闲页:通过空闲页链表(Free List)管理。

2、已被使用的正常页:通过LRU链表(LRU List)管理。

3、已被使用的脏页:通过LRU链表和脏页链表(Flush List)管理。

脏页(dirty page):指缓冲池中被修改过的页,与磁盘上的数据页不一致。

Buffer Pool 中的空闲页通过空闲页链表管理,每当从磁盘中读取数据页缓存到Buffer Pool中时,从空闲页链表取一个空闲页使用,并从空闲链表中移除。

当需要将脏页刷盘时,遍历脏页链表,将脏页写入磁盘。

已被使用的正常页通过LRU链表管理,其中LRU链表上也有脏页

缓冲池的大小是有限的,比如磁盘有50G,内存8G,而缓冲池只有1G。是无法将磁盘的数据全部放在缓冲池里,需要根据优先级来缓存数据,会优先对使用频次高的热数据进行加载

你可以在 show engine innodb status 结果中,查看一个系统当前的 BP 命中率。一般情况下,一个稳定服务的线上系统,要保证响应时间符合要求的话,内存命中率要在 99% 以上。

执行 show engine innodb status ,可以看到Buffer pool hit rate字样,显示的就是当前的命中率。比如 这个命中率,就是 99.0%。

基础 LRU 算法

InnoDB 内存管理用的是最近最少使用 (Least Recently Used, LRU) 算法,这个算法的核心就是淘汰最久未使用的数据。InnoDB 管理 Buffer Pool 的 LRU 算法,是用链表来实现的。

LRU链表

链表头节点存储最近使用的数据,链表尾部节点存储最久未被使用的数据。淘汰时删除链表尾部节点。

LRU链表的基本操作:

1、state1状态:P1是最近使用的页,Pm是最久未被使用的页。

2、state2状态:当有读请求访问P3页,P3移动到head节点。

3、state3状态:假设Px页不在buffer pool中,而此时内存已经满了,需要先淘汰掉LRU尾结点(最久未被使用的Pm),再将Px放到head节点。

InnoDB 改进后的 LRU 算法

实际上, InnoDB 引擎对LRU算法做了改进。为什么没有直接使用基础LRU算法呢?

试想一下,如果要对一个很大的表做全表扫描,这个表是一个冷数据表(平时没有业务访问它)。如果使用基础的LRU算法,会 把Buffer Pool里的数据全部淘汰掉,存储的都是冷数据表的数据页。

这会导致Buffer Pool的内存命中率急剧下降,并且由于内存中没有热点数据,导致大量磁盘IO增加磁盘压力,SQL语句响应变慢。

InnoDB 引擎改进后的LRU算法如下:

InnoDB 将LRU链表按照5:3的比例分成了young区域old区域。链表头部的5/8是young区,链表尾部的3/8区域是old区域

old区域占整个LRU链表的长度由参数innodb_old_blocks_pc控制

1、当要访问P3页时,P3在young区直接移动到链表头结点。(state1 -> state2)

2、当要访问不存在Buffer Pool上的页时(Px),删除old区域尾结点Pm,但是与基础LRU算法不同的是,Px插入在old区域的“头结点”。

3、处于old区域的数据页,每次被访问到时,都需要做下面这个判断:

  • 若这个数据页在LRU链表的时间超过了1秒,说明它是热点数据,将其移动到LRU链表young区域的头结点。
  • 若这个数据页再LRU链表的时间小于1秒,则不做任何处理。
  • 1秒这个时间是由参数innodb_old_blocks_time控制的,默认是1000ms

以上, InnoDB 改进后的LRU算法,在做全表扫描时,过程如下:

1、扫描过程中,需要新插入的数据页,都放到old区域

2、数据页中存储的是多条记录,因此这个数据页会被多次访问到,但由于是顺序扫描,这个数据页第一次被访问和最后一次被访问的时间间隔不会超过 1 秒,因此这个数据页还是会被保留在 old 区域;

3、继续扫描后续的数据页,之前的这个数据页之后也不会再被访问到,它不会移动到young区域的链表头部,很快就会被淘汰出去。

改进后的LRU算法,即使再做大表全表扫描时,对LRU链表的young区域也没有影响,保证了Buffer Pool的命中率。

引申问题:全表扫描对server的影响

问题:如果对一个200G的做全表扫描,会不会把mysql服务器内存用光?

实际上mysql server并不会保存完整的结果集。MySQL是“边读边发”的。server读取的数据会写到net_buffer 内存块中,net_buffer的默认大小是16KB,内存块写满了就会将结果发送给客户端,客户端成功接收后,就会清空net_buffer,继续读取、发送数据。

多个Buffer Pool实例

Buffer pool 本质是 InnoDB 向操作系统申请一块连续的内存空间。

多线程并发情况访问,单一的Buffer pool 会影响请求的处理速度。因为涉及到竞争问题。所以在Buffer pool 很大的时候,将其拆分成若干个小的Buffer pool ,每个Buffer pool 都是一个实例,独立申请内存空间,独立管理数据。多线程并发访问时,不会互相影响,提高并发处理能力。

通过 Innodb_buffer_pool_instances 参数修改实例个数。通过innodb_buffer_pool_size参数查看缓冲池的大小。

每个buffer pool 占用多少存储空间?

innodb_buffer_pool_size / Innodb_buffer_pool_instances

实例越多越好吗?

buffer pool实例不是越多越好,管理各个buffer pool也需要开销的。

InnoDB 规定:当innodb_buffer_pool_size小于1G时,多个实例是无效的。 InnoDB 会默认把Innodb_buffer_pool_instances设置为1.

Buffer Pool 刷盘机制

问题:SQL语句更新了缓冲池的数据,数据会马上同步到磁盘上吗?

当我们对数据库中的记录进行修改时,首先会修改缓冲池中页的记录信息。然后数据库会以一定的频率刷新到磁盘上。也就是checkpoint 机制。这样做的好处是提升数据库的整体性能。

当缓冲池不够用时,需要释放掉一些不常用的页,此时可以采用checkpoint机制,将不常用的脏页 回写到磁盘,然后再释放掉缓冲池中的内存。

脏页(dirty page):指缓冲池中被修改过的页,与磁盘上的数据页不一致。

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

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

Buffer Pool 引申问题

问题1:如果buffer pool中数据修改成功,但是还没来得及刷盘,mysql宕机了。数据没有持久化,怎么办?

Redo Log

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

问题2:数据更新到一半断电了,要回滚到更新之前的版本,怎么办? Undo Log

其他的内存部件

InnoDB引擎的内存区域,除了缓冲池buffer pool外,还有重做日志缓冲和额外的内存池。

重做日志缓冲 redo log buffer

InnoDB先将redo log放到重做日志缓冲区,然后按照一定的频率将其刷到重做日志文件。

缓冲区大小由innodb_log_buffer_size控制,默认是8MB。

以下3种情况会将redo log buffer中的内容刷到磁盘的redo log文件中:

  1. Master Thread每1秒,将redo log buffer中的内容刷到磁盘redo log文件。
  2. 每个事务提交时,会将redo log buffer刷到磁盘redo log文件。
  3. 当redo log buffer剩余空间小于1/2时,将redo log buffer刷到磁盘redo log文件。

额外的内存池

在对一些数据结构本身的内存进行分配时,需要从额外的内存池中申请内存。当该区域内存不够时,会从buffer pool中申请内存。

例如:记录buffer pool的LRU、锁等信息的缓冲控制块对象,这个对象的内存需要从额外内存池申请。如果额外的内存池不够,从buffer pool中申请内存。

因此,如果申请了很大的 InnoDB 缓冲池时,也应考虑相应增加这个值。


参考资料:

《MySQL 实战45讲》

《从根上理解MySQL》

《MySQL技术内存 InnoDB 存储引擎》

《SQL 必知必会》


InnoDB Buffer Pool 缓冲池详解相关推荐

  1. 重学MySQL(InnoDB Buffer Pool是什么?)

    文章目录 InnoDB Buffer Pool是什么? 我们的数据是如何放在InnoDB Buffer Pool中的? InnoDB怎么知道数据页是否在Buffer Pool中? InnoDB Buf ...

  2. MySQL · 性能优化· InnoDB buffer pool flush策略漫谈

    MySQL · 性能优化· InnoDB buffer pool flush策略漫谈 背景 我们知道InnoDB使用buffer pool来缓存从磁盘读取到内存的数据页.buffer pool通常由数 ...

  3. MySQL 引擎特性 · InnoDB Buffer Pool

    前言 用户对数据库的最基本要求就是能高效的读取和存储数据,但是读写数据都涉及到与低速的设备交互,为了弥补两者之间的速度差异,所有数据库都有缓存池,用来管理相应的数据页,提高数据库的效率,当然也因为引入 ...

  4. [转]MySQL innodb buffer pool

    最近在对公司的 MySQL 服务器做性能优化, 一直对 innodb 的内存使用方式不是很清楚, 乘这机会做点总结. 在配置 MySQL 的时候, 一般都会需要设置 innodb_buffer_poo ...

  5. Innodb Buffer Pool的三种Page和链表

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 王航威 来源 | 公众号「yangyidba」 ...

  6. 14.6.3.1 The InnoDB Buffer Pool

    14.6.3.1 The InnoDB Buffer PoolInnoDB 保持一个存储区域被称为buffer pool 用于cache数据和索引在内存里,知道InnoDB buffer pool 如 ...

  7. 14.4.3.5 Configuring InnoDB Buffer Pool Flushing 配置InnoDB Buffer Pool 刷新:

    14.4.3.5 Configuring InnoDB Buffer Pool Flushing 配置InnoDB Buffer Pool 刷新:InnoDB执行某些任务在后台, 包括flush 脏数 ...

  8. Go 语言 bytes.Buffer 源码详解之1

    转载地址:Go 语言 bytes.Buffer 源码详解之1 - lifelmy的博客 前言 前面一篇文章 Go语言 strings.Reader 源码详解,我们对 strings 包中的 Reade ...

  9. Mysql InnoDB存储引擎【二】InnoDB Buffer Pool 缓存池

    一.Innodb 架构图 1.整体架构图-内存结构 & 磁盘结构 二.Buffer Pool 缓存池 1. 缓存池实现 采用页面链表表(划分可能容纳多行的页) + 中点插入策略(新增加的页插入 ...

  10. C# Buffer.BlockCopy方法详解

    因工作需要,学习了C#方面的知识,看到Buffer.BlockCopy方法.从微软平台看到的解释第一时间没有看懂,感觉是其中的例子详解不充分.自己推理了一下,如下分享我自己的理解过程:(初学者欢迎查正 ...

最新文章

  1. nuxt 服务器构建因太耗CPU进程被杀解决办法
  2. 关于WPF装饰器的笔记
  3. Flyway 数据库版本管理控制
  4. Privoxy教程使用详解
  5. 浅谈分块二元Hermite插值
  6. 怎么设置台式计算机密码忘了,台式电脑忘记开机密码怎么办
  7. 如何提供网络冗余和稳定连接
  8. 身份证阅读器身份证读卡器Linux系统二次开发包(含Linux身份证相片解码库)
  9. 【PP那些事儿】生产模式-面向订单生产
  10. Twitterrific for Mac(Twitter客户端)
  11. 基于Halcon学习的新能源车牌识别【三】
  12. java基础——多态
  13. 逆水寒服务器怎么全维护,《逆水寒》2020年1月22日更新公告
  14. 互联网夜高峰,无人应答
  15. 黑苹果OC配置工具:OpenCore Configurator for Mac(2.48.0.0中文)
  16. 将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I
  17. [18调剂]北方民族大学2018年硕士研究生调剂公告
  18. 3*3*3魔方旋转算法
  19. BD功率变送器在造纸磨浆机控制系统中的研究应用
  20. matlab二维相关随机变量样本点,生成包含N=100个二维样本的数据集

热门文章

  1. 区块链学习——HyperLedger-Fabric v0.6环境搭建详细过程
  2. Linux Mint(version19)
  3. php 卡路里计算,热量换算_懒人工具|www.ab173.com
  4. 书写NDIS过滤钩子驱动实现ip包过滤
  5. c语言高级成分,高级语言的基本成分数据成分,运算成分,控制成分,传输成分,怎么看它们的类型区别的?比如其中对处理对象的类型说明属于高级语...
  6. 【云原生】SpringCloud系列之服务调用OpenFeign(基本概念和使用步骤)
  7. python interpreter下载_Pyonic Python 2 interpreter
  8. 【python】python基础与unittest基础
  9. ArcGIS实验教程——实验二十:ArcGIS数字高程模型DEM建立
  10. keil 生成三角波dac0832_怎么样利用南方CASS三角网法和方格网法进行土方量计算...