MySQL的InnoDB缓冲池

概述

       缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。写入时,先将数据写入缓冲池种,再定期刷新到磁盘;读取时,将读到的页放到缓冲池种,下次再读取相同的页时,会首先判断该页是否在缓冲池中,若再缓冲池中则直接从缓冲池中返回。

       从InnoDB 1.0.X版本开始,允许有多个缓冲池实例,每个页根据哈希值平均分配到不同缓冲池实例中,这样做的好处是减少数据库内部的资源竞争,增加数据库的并发处理能力,可以通过参数innodb_buffer_pool_instances来进行配置。

mysql> show variables like 'innodb_buffer_pool_instances'\G;
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_instances
        Value: 1
1 row in set (0.00 sec)

将参数调为2,运行show engine innodb status\G;  可看到2个缓冲池已开启(这里遇到一个坑,只改instances参数,试了很多遍还是只有一个缓冲池,后来发现将缓冲池大小调整为1G以上才生效)

----------------------
INDIVIDUAL BUFFER POOL INFO
----------------------
---BUFFER POOL 0
Buffer pool size   40960
Free buffers       40420
Database pages     537
Old database pages 218
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 466, created 71, written 77
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 537, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
---BUFFER POOL 1
Buffer pool size   40960
Free buffers       40520
Database pages     436
Old database pages 0
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 363, created 73, written 81
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 436, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

缓冲池页大小

       再InnoDB存储引擎中,缓冲池中页的默认大小为16KB,从1.0.X版本开始支持压缩页的功能,将原本16KB的页压缩为1KB、2KB、4KB、8KB。对于非16KB的页,是通过unzip_LRU列表进行管理的,通过show engine innodb status可以看到如下内容:

No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 537, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

LRU中的页包含unzip_LRU列表中的页,假如需要申请页为4KB的大小,步骤如下;

  1. 检查4KB的unzip_LRU列表,检查是否有可用的空闲页,若有,则直接使用
  2. 否则,检查8KB的unzip_LRU列表,若有,则将页分成2个4KB页,存放到4KB的unzip_LRU列表
  3. 若还不能得到空闲页,从LRU列表申请一个16KB的页,将页分成1个8KB和2个4KB,分别存放到对应unzip_LRU列表中

缓冲池LRU算法

       InnoDB存储引擎对传统的LRU算法做了一些优化,LRU列表中加入了midpoint位置。新读取到的页,虽然是最新访的页,但并不是直接放到LRU列表的首部,而是放到LRU列表的midpoint位置。这个算法在InnoDB存储引擎下称为midpoint insertion strategy。在默认配置下,该位置在LRU列表长度的5/8处,即百分之37,可由参数innodb_old_blocks_pct控制。

mysql> show variables like 'innodb_old_blocks_pct'\G;
*************************** 1. row ***************************
Variable_name: innodb_old_blocks_pct
        Value: 37
1 row in set (0.13 sec)

       上面可看到默认为37,表示新读取的页插入到LRU列表尾端的百分之37的位置。把midpoint之后的列表称为old列表,之前的列表称为new列表。

       这么做是因为若直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池的页被刷新出去,从而影响缓冲池的效率。常见的这类操作为索引或数据的扫表操作,这类操作需要访问列表中的很多页,甚至是全部的页,而这些页又仅仅在这次操作中需要,并不是活跃的热点数据。如果直接放到LRU首部,那么肥场可能将其他热点数据从LRU全部移除,而在下一次需要读取该页时,InnoDB引擎需要再次访问磁盘。

       同时,InnoDB存储引擎还引入了另一个参数innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。

mysql> mysqlvariables like 'innodb_old_blocks_time'\G;
*************************** 1. row ***************************
Variable_name: innodb_old_blocks_time
        Value: 1000
1 row in set (0.00 sec)

      当页通过LRU列表的old部分进入到new部分时,会记录youngs,而因为超时没有进入到new部分,会记录not young

Pages made young 6448526, not young 0
48.75 youngs/s, 0.00 non-youngs/s 
Pages read 5354420 created 239625, written 3486063
55.68 reads/s, 81.74 creates/s, 955.88 writes/s
Buffer pool hit rate 1000/1000, young-making rate 0/1000, not 0/1000

non-youngs/s的值过大原因

  • 可能存在严重的全表扫描,频繁的被刷出来
  • 可能是pct设置的过小,冷数据区很小,来一点数据就刷出去了
  • 可能是time设置的过大,没坚持到,被刷出去了

youngs/s的值过大的原因,正常不可能一直很高,因为热数据区就那么大,不可能一直往里调。

  • pct过大
  • time过小

如果缓存命中率过低,需要排查是否由于全表扫描引起的LRU列表被污染的问题。

发布了200 篇原创文章 · 获赞 26 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/why444216978/article/details/103217933
今日推荐