InnoDB存储引擎Buffer Pool

简介

InnoDB存储引擎是基于磁盘存储的,由于CPU速度与磁盘速度之间天差地别,因此引入缓冲技术来提高整体性能。

缓冲池,是主存中InnoDB缓存被访问的表和索引数据的区域。缓冲池允许直接从内存中处理经常使用的数据,从而加快处理速度。

例如,客户端进行读取、修改操作:

  • 读取操作:首先从磁盘上读取到的页放在缓冲池中,下一次读取相同的页时,先判断该页是否在缓冲池中,若在,则称作页在缓冲池被命中,否则需要去磁盘上读取;
  • 修改操作:首先修改在缓冲池中的页(修改过的页,称为脏页),然后通过Checkpoint机制,按照一定的频率刷新到磁盘上。

在专用服务器上,高达80%的物理内存通常分配给缓冲池。
可以通过innodb_buffer_pool_size配置缓冲池的大小(默认8M),还可以通过innodb_buffer_pool_instances配置缓冲池的数量(默认1个)。

主要注意的是,缓冲池大小innodb_buffer_pool_size必须始终等于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances * K(K是个大于0的常数),因此如果设置了个不满足上述条件的innodb_buffer_pool_size,innoDB会帮你自动调整为最接近上述条件的一个值。

缓冲池缓存数据对象

image.png

为了提高大容量读操作的效率,缓冲池被划分为可以容纳多行的页。
如图所示,innoDB缓冲池缓存的数据对象主要有:

  • 数据页;
  • 索引页;
  • undo页;
  • 变更缓冲;
  • 自适应哈希索引;
  • 数据字典;
  • 锁信息;

缓冲池管理

为了提高缓存管理的效率,缓冲池被实现为页链表,使用LRU算法来进行管理,与传统LRU算法不同的是,InnoDB存储引擎对LRU做了一些优化。当需要向缓冲池中添加新页时,将淘汰最近最少使用的页,并将一个新页添加到列表的midpoint位置,而不是放在首部。这种中点插入策略将LRU划分为两个子列表:

  • New Sublist:在midpoint前面,new子列表,即热点页;
  • Old Sublist:在midpoint后面,old子列表。


    image

如图所示,new子列表占用5/8的空间,midpoint正处于列表3/8的位置,old子列表占用3/8的空间。可以通过innodb_old_blocks_pct(默认37,按百分比算=3/8)配置midpoint的位置。

算法逻辑:假设用户读取某个缓冲池中没有的页。

  • 先去磁盘读取页到缓冲池;
  • 该页对LRU来说是一个新的页,因此会把它加到midpoint,也就是old子列表的首部;
  • 如果该页在之后的某段时间被访问了,LRU算法会认为它是热点页,将它加到new子列表的首部,这个操作被称为page made young

InnoDB还提供一个配置参数innodb_old_blocks_time延迟新插入的页,被加到new子列表的首部的时间。比如说,假设设置了innodb_old_blocks_time=1000,这个时候有个新页被加到midpoint位置,在1000ms期间,无论该页被访问多少次,都没办法被加到new子列表的首部。这个操作被称为page not made young

为什么InnoDB不使用传统的LRU算法?答案很明显,假设某张学生表有1000个学生,现在通过select * from student where name = '张三',这条sql做了学生表的全表扫描,innoDB会将扫到的页都存放到缓冲池中,也就是加到LRU。如果是传统LRU,则扫描到一个页,就加到首部。因此把大量的页(可能这些页只用一次,后面就不再使用了)放到了首部,导致原先热点数据被挤出来。

查看缓冲池的运行状态

可以通过SHOW ENGINE INNODB STATUS;查看。

image

参数名称 描述
Total memory allocated 分配给缓冲池的总内存,以字节为单位
Dictionary memory allocated 分配给InnoDB数据字典的总内存,以字节为单位
Buffer pool size 分配给缓冲池的页的总大小
Free buffers 缓冲池空闲列表中页的总大小
Database pages 缓冲池LRU列表中的页的总大小
Old database pages 缓冲池LRU旧子列表中的页的总大小
Modified db pages 缓冲池中当前修改的页数
Pending reads 等待读入缓冲池的缓冲池页数
Pending writes LRU 缓冲池中从LRU列表底部等待写入的旧脏页数量
Pending writes flush list 在检查点期间要刷新的缓冲池脏页的数目
Pending writes single page 缓冲池中等待写入的独立页数量
Pages made young 在缓冲池LRU列表中变为年轻的页面总数
Pages made not young 缓冲池LRU列表中未变为年轻的页面总数
youngs/s 变年轻的频率
non-youngs/s 没有变年轻的频率
Pages read 从缓冲池中读取的页面总数
Pages created 在缓冲池中创建的页面总数
Pages written 从缓冲池写入的页面总数
reads/s 从缓冲池中读取的页面频率
creates/s 在缓冲池中创建的页面频率
writtes/s 从缓冲池写入的页面频率
Buffer pool hit rate 缓冲池页命中率
young-making rate 页面访问导致页面年轻的平均命中率
not (young-making rate) 页面访问没有导致页面年轻的平均命中率
Pages read ahead 预读操作的每秒平均次数
Pages evicted without access 被淘汰,而无法从缓冲池中访问页面的每秒平均数量
Random read ahead 随机预读操作的每秒平均次数
LRU len 缓冲池LRU列表中的页的总大小
unzip_LRU len 缓冲池解压缩LRU列表中页的总大小
I/O sum 最近50秒内访问缓冲池LRU列表页的总数
I/O cur 访问缓冲池LRU列表页的总数
I/O unzip sum 最近50秒内访问缓冲池解压缩LRU列表页的总数
I/O unzip cur 访问缓冲池解压缩LRU列表页的总数

从上面可知,‘Free buffers’代表的是缓冲池空闲列表中页的总大小,当数据库启动时,LRU的列表是空的,即没有任何页。这时,页都放到Free List中,当需要从缓冲池中分配页时,首先从Free List中查找是否有空闲页,如果有则从Free List移到LRU 列表中;否则,根据LRU算法,淘汰末尾的页,腾出来给新的页用。

其次,‘Pending writes flush list’表示脏页列表的总页数,在LRU列表中的页被修改后,成为脏页。Flush list是来管理脏页列表,负责将脏页刷回磁盘。当然LRU列表也会存储在脏页,保证缓冲池页的可用性,两者并不冲突。

另外,有一个重要的参数‘Buffer pool hit rate’命中率等于100%,说明缓冲池运行状态非常好,通常来讲,该值不应该小于95%,如果发生这种情况,考虑是否是因为全表扫描引起的LRU被污染的问题,可以适当调整innodb_old_blocks_time大小,或者避免全表扫描。

最后,看下‘LRU len: 5720, unzip_LRU len: 0’。‘LRU lenInnoDB’代表LRU列表页的总数量(包含unzip_LRU),‘unzip_LRU’代表解压缩(说明被压缩过了,才需要解压缩)LRU列表中页的总大小。缓冲池页的大小默认是16K,对于非16K(原本16K的页压缩为1K,2K,4K,8K),是通过unzip_LRU列表进行管理。unzip_LRU管理方式如下:假设需要申请一个4K的页。

  • 检查unzip_LRU列表是否有空闲的4K页,有则直接使用;如果没有,则往下走;
  • 检查是否有空闲的8K页,有则切割成两个4K页,一个直接使用,一个存放到unzip_LRU列表;如果没有,则往下走;
  • 从LRU列表申请一个16K的空闲页,切割成一个8K,两个4K,分别存放到unzip_LRU列表中。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容