龙空技术网

MySQL的缓冲池「Buffer Pool」你知道多少?

波哥带你学Java 150

前言:

现时兄弟们对“mysql怎么清除缓存啊”大约比较看重,大家都想要分析一些“mysql怎么清除缓存啊”的相关文章。那么小编在网络上搜集了一些关于“mysql怎么清除缓存啊””的相关文章,希望朋友们能喜欢,看官们一起来了解一下吧!

请添加图片描述

1.简介

  buffer pool 就是一个缓存,将磁盘中的数据缓存到内存中,对数据的操作改为通过内存进行操作,然后刷盘的操作,提升性能。

innodb_buffer_pool_size 控制缓存池的大小。

2.内部架构

在这里插入图片描述

3. 描述信息

在这里插入图片描述

4.free 链表

  当我们初始化 buffer pool 之后,会不断的有数据被从磁盘中缓存到 buffer pool 里,但是我们要怎么知道那些缓存页还没有被使用呢 ?

  free 链表的主要作用就是维护空闲的缓存页的节点信息

在这里插入图片描述

  free 链表维护了处于空闲状态的缓存页对应的描述信息,组成了一个双向链表,会有一个基节点,里面存储了空闲节点的个数和头尾节点,同时,基节点是另外申请的一块大约40字节的内存空间,不会占用 buffer pool 的内存. 5.flush 链表

在这里插入图片描述

  基本结构和 free 链表 是一致的,图就不换了,我们在更新一条数据的时候,首先会更新的是 buffer pool 中的数据,这样的话,就导致了缓存中的数据和磁盘中数据不一致的情况,出现这种情况的缓存页我们叫做 脏页 ,脏页的话需要刷新会磁盘中去,所以 flush 链表 主要存放的就是这些 脏页 。

6.哈希表

  在 Buffer Pool 有一个专门的哈希表,存储 表空间+页号 为 key , 缓存页地址 为 value 的哈希表,每次读取数据的时候,会先从哈希表中获取,找不到的话,才会从磁盘中将数据缓存到 Buffer Pool 里。

7. lru 链表因为我们的 Buffer Pool 是受参数影响的,只能容纳参数范围内的数据,因此,我们需要有一个 清除(刷盘)策略 ,将缓存页中的数据刷回到磁盘中去,释放缓存页,将其加入到 free 链表 中,便于下一次从磁盘缓存数据的时候,有空闲的缓存页可以供其使用。lru 代表最少使用,我们会将命中缓存的数据,放到链表的头部,那么长时间无人使用的缓存页就会被放到链表的尾部,若没有空闲的缓存页的时候,会将链表尾部的缓存中刷新会磁盘,释放缓存页空间,并将该缓存页对应的描述信息添加到 free 链表 中去。因为 预读 机制的存在,会将一部分 mysql 认为可能下次会读到的数据也加载到缓存页中,因此单纯简单的 lru 链表就会导致一部分后期可能不会读到的数据(例如预读到的数据,或者全表检索的数据),加入到 lru 的头部,可能会将一些频繁访问的数据,挤到后面,导致数据被刷盘,因此,提供了一种 冷热分离 的 lru 链表

在这里插入图片描述

lru 链表会将这块区域分为两个部分,热数据区和冷数据区,由参数 innodb_old_blocks_pct 控制,默认为 37 ,也就是冷数据区占整个内存空间的 37%;从磁盘中加载到的数据,首先会放到 lru 的 冷数据区 的头部,若 在 innodb_old_block_time 间隔之后,还命中了这个缓存页,会将其从冷数据区放到 热数据区 的头部,若在这个时间之前的操作,只是会移动到 冷数据区的头部当然,也不是每次操作都会移动,这样的话,太消耗性能,规定的是 只有命中热数据区的后四分之三时才会将其移动到热数据区的头部,若命中的缓存页处于热数据区的四分之一前,是不会进行移动的8.刷新数据到磁盘会有线程定期的从 flush 链表 中,获取到 脏页 信息,将其刷新回磁盘中,同时将该缓存页对象的描述信息,加入到 free 链表 中会有线程定期从 lru 链表中 中,从尾部节点臊面数据,由参数 innodb_lru_scan_depth 控制扫描多少。如果发现脏页会进行刷盘操作,同时将该缓存页对象的描述信息,加入到 free 链表 中有时候,当我们从磁盘缓存到 buffer pool 中,发现没有空闲缓存页的时候,首先会看 Lru 链表尾部有没有未修改的可以直接释放的页面,要是没有的话,只能将尾部的脏页同步到盘里.

在这里插入图片描述

在这里插入图片描述

9. 多个 Buffer Pool 实例因为我们对 Buffer Pool 的操作,是需要 加锁的 ,因此单个 Buffer Pool 会对性能进行限制,所以我们可以将一块 buffer pool 拆分成多个小 buffer pool 的实例,各自独立通过 innodb_buffer_pool_instance 参数控制,但如果 innodb_buffer_pool_size 小于 1G 的话,只允许有一个 buffer pool 实例在 MySQL 5.7.5 之前,是不允许动态调整 buffer pool 的大小的,因为每次调整大小需要重新申请内存空间,并将之前的数据移动到新空间里去,会有很大的性能消耗,因此,在之后更新出了 chunk 的概念每个 buffer pool 实例中,其实是多个由 chunk 组成的,里面包含各自的描述信息,缓存页等信息,这样我们就可以以 chunk 为单位进行调整整个 Buffer Pool 的空间,由参数 innodb_buffer_pool_chunk_size 控制,默认128M,不允许进行动态调整

注意事项

innodb_buffer_pool_size 必须是 innodb_buffer_pool_chunk_size * innodb_buffer_instances 的倍数,如果设置的不是倍数,MySQL 会自动设置为倍数。

mysqld --innodb-buffer-pool-size=8G --innodb-buffer-pool-instances=16如果 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的值已经大于innodb_buffer_pool_size的值,那么innodb_buffer_pool_chunk_size的值会被服务器自动设置为innodb_buffer_pool_size/innodb_buffer_pool_instances的值。10.查看信息

mysql> SHOW ENGINE INNODB STATUS\G。。。 。。。----------------------BUFFER POOL AND MEMORY----------------------Total memory allocated 13218349056;Dictionary memory allocated 4014231Buffer pool size   786432Free buffers       8174Database pages     710576Old database pages 262143Modified db pages  124941Pending reads 0Pending writes: LRU 0, flush list 0, single page 0Pages made young 6195930012, not young 78247510485108.18 youngs/s, 226.15 non-youngs/sPages read 2748866728, created 29217873, written 4845680877160.77 reads/s, 3.80 creates/s, 190.16 writes/sBuffer pool hit rate 956 / 1000, young-making rate 30 / 1000 not 605 / 1000Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/sLRU len: 710576, unzip_LRU len: 118I/O sum[134264]:cur[144], unzip sum[16]:cur[0]。。。 。。。
Total memory allocated:代表Buffer Pool向操作系统申请的连续内存空间大小,包括全部控制块、缓存页、以及碎片的大小。Dictionary memory allocated:为数据字典信息分配的内存空间大小,注意这个内存空间和Buffer Pool没啥关系,不包括在Total memory allocated中。Buffer pool size:代表该Buffer Pool可以容纳多少缓存页,注意,单位是页!Free buffers:代表当前Buffer Pool还有多少空闲缓存页,也就是free链表中还有多少个节点。Database pages:代表LRU链表中的页的数量,包含young和old两个区域的节点数量。Old database pages:代表LRU链表old区域的节点数量。Modified db pages:代表脏页数量,也就是flush链表中节点的数量。Pending reads:正在等待从磁盘上加载到Buffer Pool中的页面数量。

当准备从磁盘中加载某个页面时,会先为这个页面在Buffer Pool中分配一个缓存页以及它对应的控制块,然后把这个控制块添加到LRU的old区域的头部,但是这个时候真正的磁盘页并没有被加载进来,Pending reads的值会跟着加1。Pending writes LRU:即将从LRU链表中刷新到磁盘中的页面数量。Pending writes flush list:即将从flush链表中刷新到磁盘中的页面数量。Pending writes single page:即将以单个页面的形式刷新到磁盘中的页面数量。Pages made young:代表LRU链表中曾经从old区域移动到young区域头部的节点数量。

  这里需要注意,一个节点每次只有从冷数据区域移动到热数据区域头部时才会将Pages made young的值加1,也就是说如果该节点本来就在热数据区域,由于它符合在热数据区域1/4后边的要求,下一次访问这个页面时也会将它移动到young区域头部,但这个过程并不会导致Pages made young的值加1。

Page made not young:在将innodb_old_blocks_time设置的值大于0时,首次访问或者后续访问某个处在old区域的节点时由于不符合时间间隔的限制而不能将其移动到young区域头部时,Page made not young的值会加1。

这里需要注意,对于处在young区域的节点,如果由于它在young区域的1/4处而导致它没有被移动到young区域头部,这样的访问并不会将Page made not young的值加1。youngs/s:代表每秒从old区域被移动到young区域头部的节点数量。non-youngs/s:代表每秒由于不满足时间限制而不能从old区域移动到young区域头部的节点数量。Pages read、created、written:代表读取,创建,写入了多少页。后边跟着读取、创建、写入的速率。Buffer pool hit rate:表示在过去某段时间,平均访问1000次页面,有多少次该页面已经被缓存到Buffer Pool了。young-making rate:表示在过去某段时间,平均访问1000次页面,有多少次访问使页面移动到young区域的头部了。

需要大家注意的一点是,这里统计的将页面移动到young区域的头部次数不仅仅包含从old区域移动到young区域头部的次数,还包括从young区域移动到young区域头部的次数(访问某个young区域的节点,只要该节点在young区域的1/4处往后,就会把它移动到young区域的头部)。not (young-making rate):表示在过去某段时间,平均访问1000次页面,有多少次访问没有使页面移动到young区域的头部。

需要大家注意的一点是,这里统计的没有将页面移动到young区域的头部次数不仅仅包含因为设置了innodb_old_blocks_time系统变量而导致访问了old区域中的节点但没把它们移动到young区域的次数,还包含因为该节点在young区域的前1/4处而没有被移动到young区域头部的次数。LRU len:代表LRU链表中节点的数量。unzip_LRU:代表unzip_LRU链表中节点的数量(由于我们没有具体唠叨过这个链表,现在可以忽略它的值)。I/O sum:最近50s读取磁盘页的总数。I/O cur:现在正在读取的磁盘页数量。I/O unzip sum:最近50s解压的页面数量。I/O unzip cur:正在解压的页面数量。

请添加图片描述

标签: #mysql怎么清除缓存啊