您现在的位置是:网站首页> 编程资料编程资料

mysql的Buffer Pool存储及原理解析_Mysql_

2023-05-26 467人已围观

简介 mysql的Buffer Pool存储及原理解析_Mysql_

一、前言

1、buffer pool是什么

咱们在使用mysql的时候,比如很简单的select * from table;这条语句,具体查询数据其实是在存储引擎中实现的,数据库中的数据实际上最终都是要存放在磁盘文件上的,如果每次查询都直接从磁盘里面查询,这样势必会很影响性能,所以一定是先把数据从磁盘中取出,然后放在内存中,下次查询直接从内存中来取。
但是一台机器中往往不是只有mysql一个进程在运行的,很多个进程都需要使用内存,所以mysql中会有一个专门的内存区域来处理这些数据,这个专门为mysql准备的区域,就叫buffer pool。
buffer pool是mysql一个非常关键的核心组件。

如下图所示:

在对数据库执行增删改操作的时候,不可能直接更新磁盘上的数据的,因为如果你对磁盘进行随机读写操作,那速度是相当的慢,随便一个大磁盘文件的随机读写操作,可能都要几百毫秒。如果要是那么搞的话,可能你的数据库每秒也就只能处理几百个请求了! 在对数据库执行增删改操作的时候,实际上主要都是针对内存里的Buffer Pool中的数据进行的,也就是实际上主要是对**数据库的内存(Buffer Pool)**里的数据结构进行了增删改。
如下图所示:

操作内存的主要问题就是在数据库的内存里执行了一堆增删改的操作,内存数据是更新了,但是这个时候如果数据库突然崩溃了,那么内存里更新好的数据不是都没了吗? MySQL就怕这个问题,所以引入了一个redo log机制,你在对内存里的数据进行增删改的时候,他同时会把增删改对应的日志写入redo log中。

如下图:

万一数据库突然崩溃了,没关系,只要从redo log日志文件里读取出来你之前做过哪些增删改操作,瞬间就可以重新把这些增删改操作在你的内存里执行一遍,这就可以恢复出来你之前做过哪些增删改操作了。
当然对于数据更新的过程,它是有一套严密的步骤的,还涉及到undo log、binlog、提交事务、buffer pool脏数据刷回磁盘等等。

小结:

Buffer Pool就是数据库的一个内存组件,里面缓存了磁盘上的真实数据,然后我们的系统对数据库执行的增删改操作,其实主要就是对这个内存数据结构中的缓存数据执行的。通过这种方式,保证每个更新请求,尽量就是只更新内存,然后往磁盘顺序写日志文件。
更新内存的性能是极高的,然后顺序写磁盘上的日志文件的性能也是比较高的,因为顺序写磁盘文件,他的性能要远高于随机读写磁盘文件。

2、buffer pool的工作流程

以查询语句为例

  • 在查询的时候会先去buffer pool(内存)中看看有没有对应的数据页,如果有的话直接返回
  • 如果buffer pool中没有对应的数据页,则会去磁盘中查找,磁盘中如果找到了对应的数据,则会把该页的数据直接copy一份到buffer pool中返回给客户端
  • 下次有同样的查询进来直接查找buffer pool找到对应的数据返回即可。

3、buffer pool缓冲池和查询缓存(query cache)

在正式讲解buffer pool 之前,我们先搞清楚buffer pool缓冲池和查询缓存(query cache)简称Qcache的区别。

如果将Mysql分为Server层和存储引擎层两大部分,那么Qcache位于Server层,Buffer Pool位于存储引擎层。

如果Mysql 查询缓存功能是打开的,那么当一个sql进入Mysql Server之后,Mysql Server首先会从查询缓存中查看是否曾经执行过这个SQL,如果曾经执行过的话,曾经执行的查询结果之前会以key-value的形式保存在查询缓存中。key是sql语句,value是查询结果。我们将这个过程称为查询缓存。查询缓存会被所有的session共享。
如果查询缓存中没有你要找的数据的话,MySQL才会执行后续的逻辑,通过存储引擎将数据检索出来。
MySQL查询缓存是查询结果缓存。它将以SEL开头的查询与哈希表进行比较,如果匹配,则返回上一次查询的结果。进行匹配时,查询必须逐字节匹配,例如 SELECT * FROM t1; 不等于select * from t1;,此外,一些不确定的查询结果无法被缓存,任何对表的修改都会导致这些表的所有缓存无效(只要有一个sql update了该表,那么表的查询缓存就会失效)。因此,适用于查询缓存的最理想的方案是只读,特别是需要检查数百万行后仅返回数行的复杂查询。如果你的查询符合这样一个特点,开启查询缓存会提升你的查询性能。
MySQL查询缓存的目的是为了提升查询性能,但它本身也是有性能开销的。需要在合适的业务场景下(读写压力模型)使用,不合适的业务场景不但不能提升查询性能,查询缓存反而会变成MySQL的瓶颈。

查询缓存的开销主要有:

  • 读查询在开始前必须先检查是否命中缓存;
  • 如果这个读查询可以被缓存,那么当完成执行后,MySQL若发现查询缓存中没有这个查询,会将其结果存入查询缓存,这会带来额外的系统消耗;
  • 当向某个表写入数据的时候,MySQL必须将对应表的所有缓存都设置失效。如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗。

查询缓存的缺点:
首先,查询缓存的效果取决于缓存的命中率,只有命中缓存的查询效果才能有改善,因此无法预测其性能。只要有一个sql update了该表,那么表的查询缓存就会失效,所以当你的业务对表CRUD的比例不相上下,那么查询缓存会影响应用的吞吐效率。
其次,查询缓存的另一个大问题是它受到单个互斥锁的保护。在具有多个内核的服务器上,大量查询会导致大量的互斥锁争用。

注意:在mysql8.0的版本中,已经将查询缓存模块删除了。

二、buffer pool的内存数据结构

1、数据页概念

我们先了解一下数据页这个概念。它是 MySQL 抽象出来的数据单位,磁盘文件中就是存放了很多数据页,每个数据页里存放了很多行数据。
默认情况下,数据页的大小是 16kb。
所以对应的,在 Buffer Pool 中,也是以数据页为数据单位,存放着很多数据。但是我们通常叫做缓存页,因为 Buffer Pool 毕竟是一个缓冲池,并且里面的数据都是从磁盘文件中缓存到内存中。它和磁盘文件中数据页是一一对应的。

假设我们要更新一行数据,此时数据库会找到这行数据所在的数据页,然后从磁盘文件里把这行数据所在的数据页直接给加载到Buffer Pool里去。如下图。

2、那么怎么识别数据在哪个缓存页中

每个缓存页都会对应着一个描述数据块,里面包含数据页所属的表空间、数据页的编号,缓存页在 Buffer Pool 中的地址等等。
描述数据块本身也是一块数据,它的大小大概是缓存页大小的5%左右。假设你设置的buffer pool大小是128MB,实际上Buffer Pool真正的最终大小会超出一些,可能有个130多MB的样子,因为他里面还要存放每个缓存页的描述数据。
在Buffer Pool中,每个缓存页的描述数据放在最前面,然后各个缓存页放在后面。

所以Buffer Pool实际看起来大概如下:

3、buffer pool的初始化与配置

3.1、初始化

  • MySQL 启动时,会根据参数 innodb_buffer_pool_size 的值来为 Buffer Pool 分配内存区域。
  • 然后会按照缓存页的默认大小 16k 以及对应的描述数据块大小,在 Buffer Pool 中划分中一个个的缓存页和一个个的描述数据库块。
  • 注意:此时的缓存页和描述数据块都是空的,毕竟才刚启动 MySQL。

3.2、buffer pool的配置

buffer pool通常由数个内存块加上一组控制结构体对象组成。内存块的个数取决于buffer pool instance的个数,不过在5.7版本中开始默认以128M(可配置)的chunk单位分配内存块,这样做的目的是为了支持buffer pool的在线动态调整大小。
Buffer Pool默认情况下是128MB,还是有一点偏小了,我们实际生产环境下完全可以对Buffer Pool进行调整。 比如我们的数据库如果是16核32G的机器,那么你就可以给Buffer Pool分配个2GB的内存。

主要配置参数如下:

  • innodb_buffer_pool_size:这个值是设置 InnoDB Buffer Pool 的总大小;
  • innodb_buffer_pool_chunk_size:当增加或减少innodb_buffer_pool_size时,操作以块(chunk)形式执行,默认是128MB。
  • innodb_buffer_pool_instances:设置 InnoDB Buffer Pool 实例的个数,每一个实例都有自己独立的 list 管理Buffer Pool;
  • innodb_old_blocks_pct:默认 InnoDB Buffer Pool 中点的位置,默认值是37,最大100,也就是我们所谓的3/8的位置,可以自己设置。

这里面有个关系要确定一下,最好按照这个设置 innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instancesN(N>=1);
当buffer pool比较大的时候(超过1G),innodb会把buffer pool划分成几个instances,这样可以提高读写操作的并发,减少竞争。读写page都使用hash函数分配给一个instances。
当增加或者减少buffer pool大小的时候,实际上是操作的chunk。buffer pool的大小必须是innodb_buffer_pool_chunk_sizeinnodb_buffer_pool_instances的整数倍,如果不是的话,innodb会自动调整的。
比如:
如果指定的buffer pool size大小是9G,instances的个数是16,chunk默认的大小是128M,那么buffer会自动调整为10G。因为9216MB/16/128MB = 4.5不是整数倍,会自动调整为10240MB/16/128MB=5整数倍

3.3、Buffer Pool Size 设置和生效过程

理想情况下,在给服务器的其他进程留下足够的内存空间的情况下,Buffer Pool Size 应该设置的尽可能大。当 Buffer Pool Size 设置的足够大时,整个数据库就相当于存储在内存当中,当读取一次数据到 Buffer Pool Size 以后,后续的读操作就不用再访问磁盘。

设置方式:
当数据库已经启动的情况下,我们可以通过在线调整的方式修改 Buffer Pool Size 的大小。
通过以下语句:SET GLOBAL innodb_buffer_pool_size=402653184;
当执行这个语句以后,并不会立即生效,而是要等所有的事务全部执行成功以后才会生效;新的连接和事务必须等其他事务完全执行成功以后,Buffer Pool Size 设置生效以后才能够连接成功,不然会一直处于等待状态。
期间,Buffer Pool Size 要完成碎片整理,去除缓存 page 等等操作。在执行增加或者减少 Buffer Pool Size 的操作时,操作会作为一个执行块执行,innodb_buffer_pool_chunk_size 的大小会定义一个执行块的大小,默认的情况下,这个值是128M。
Buffer Pool Size 的大小最好设置为 innodb_buffer_pool_chunk_size/ innodb_buffer_pool_instances 的整数倍,而且是大于等于1。
如果我们要查 Buffer Pool 的状态的话,可以使用一下sql:
SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';
可以帮我们查看到状态。我们可以看一下增加 Buffer Pool 的时候的一个过程,再看一下减少的时候的日志,其实还是很好理解的,我们可以看成每次增大或者减少 Buffer Pool 的时候就是进行 innodb_buffer_pool_chunk 的增加或者释放,按照 innodb_buffer_pool_chunk_size 设定值的大小增加或者释放执行块。

  • 增加的过程: 增加执行块,指定新地址,将新加入的执行块加入到 free list(控制执行块的一个列表)。
  • 减少的过程: 重新整理 Buffer Pool 和空闲页,将数据从块中移除,指定新地址。

3.4、Buffer Pool Instances

在64位操作系统的情况下,可以拆分缓冲池成多个部分,这样可以在高并发的情况下最大可能的减少争用。
配置多个 Buffer Pool Instances 能在很大程度上能够提高 MySQL 在高并发的情况下处理事物的性能,优化不同连接读取缓冲页的争用。
我们可以通过设置 innodb_buffer_pool_instances 来设置 Buffer Pool Instances。当 InnoDB Buffer Pool 足够大的时候,你能够从内存中读取时候能有一个较好的性能,但是也有可能碰到多个线程同时请求缓冲池的瓶颈。这个时候设置多个 Buffer Pool Instances 能够尽量减少连接的争用。
这能够保证每次从内存读取的页都对应一个 Buffer Pool Instances,而且这种对应关系是一个随机的关系。并不是热数据存放在一个 Buffer Pool Instances下,内部也是通过 hash 算法来实现这个随机数的。每一个 Buffer Pool Instances 都有自己的 free lists,LRU 和其他的一些 Buffer Pool 的数据结构,各个 Buffer Pool Instances 是相对独立的。
innodb_buffer_pool_instances 的设置必须大于1才算得上是多配置,但是这个功能起作用的前提是innodb_buffer_pool_size 的大小必须大于1G,理想情况下 innodb_buffer_pool_instances 的每一个 instance 都保证在1G以上。

3.5、SHOW ENGINE INNODB STATUS

提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网