MySQL缓冲池buffer pool

概要

在应用系统分层结构中,我们经常会使用缓存(cache)来加速数据的访问。
同理的,MySQL作为一个存储系统,同样采用了缓存方式,而在MySQL中叫做缓冲池(buffer pool)机制,用来避免每次查询数据时进行磁盘IO。

InnoDB的缓冲池缓存什么数据?

缓存用于缓存数据加速访问,那么InnoDB主要缓存的是表数据与索引数据,既然缓存数据可以加速访问,那为何不缓存所有的数据呢?

1、数据库的出现就是为了解决数据存储问题,那么其存储的数据可想而知的大。比如磁盘可能有2T数据,但可能可使用的内存只有16G。
因此,我们需要缓存的数据一般都叫热数据

那么该如何管理缓冲池?

预读

磁盘读写,并不是按需读取,而是按页读取,一次至少读一页数据 (OS页一般为4k大小),如果未来要读取的数据在页中,那么就省去磁盘IO。

根据程序的局部性原理 ,预加载是非常有效,因此InnoDB的缓冲池也采用缓存页的方式来缓存数据,一般缓冲池的页为16k(可相当于4个OS页)。

采用什么算法管理缓冲池的页数据?

LRU(Least recently used),memcache,OS都会用LRU来进行页置换管理

LRU机制:

  • 页已经在缓冲池里,需要将页放置头部,没有新进页,所以不用淘汰尾部页。
  • 页不在缓冲池里,新进页到头部,再淘汰尾部页。

为了减少数据移动,LRU一般采用链表实现 ,像使用java实现LRU时,常用LinkedHashMap的数据结构

在MySQL中,需要针对LRU做一些优化:

1、当预读将页数据放入缓冲池LRU头部时,但MySQL并没有从该预读也读取数据,业界称为预读失效

MySQL的优化方式:

扫描二维码关注公众号,回复: 14594869 查看本文章

(1)、将LRU分为两部分

  • 新生代(new sublist)
  • 老生代(old sublist)
    (2)、新生代的尾(tail)连接着老生代的头(head)
    (3)、预读页加入到缓冲池时先加入到老生代头部,当真正预读成功时,再加到新生代的头部。如果没有被读取,则会比新生代里的数据更早淘汰出缓冲池

2、当某一个SQL语句,要批量扫描大量数据时,可能导致把缓冲池的所有页都替换出去,导致大量热数据被换出,MySQL性能急剧下降,这种情况叫缓冲池污染。

场景:
当发生全表查询或者扫描大量数据时,可能会预读大量的页,所有的数据页都会被加载到新生代的头部,但只会访问一次,真正的热数据被大量换出

MySQL的优化方式:

(1)、老生代停留时间窗口机制:

  • 假设T=老生代停留时间窗口;
  • 插入老生代头部的页,即使立刻被访问,并不会立刻放入新生代头部;
  • 只有满足“被访问”并且“在老生代停留时间”大于T,才会被放入新生代头部;

show variables like '%innodb_buffer_pool_size%';
参数:innodb_buffer_pool_size
介绍:配置缓冲池的大小,在内存允许的情况下,DBA往往会建议调大这个参数,越多数据和索引放到内存里,数据库的性能会越好。

show variables like '%innodb_old_blocks_pct%';
参数:innodb_old_blocks_pct
介绍:老生代占整个LRU链长度的比例,默认是37,即整个LRU中新生代与老生代长度比例是63:37。

show variables like '%innodb_old_blocks_time%';
参数:innodb_old_blocks_time
介绍:老生代停留时间窗口,单位是毫秒,默认是1000,即同时满足“被访问”与“在老生代停留时间超过1秒”两个条件,才会被插入到新生代头部。

总结

(1)缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;

(2)缓冲池通常以页(page)为单位缓存数据;

(3)缓冲池的常见管理算法是LRU;

(4)InnoDB对LRU进行了优化:

  • 将缓冲池分为老生代和新生代,缓冲池的新页,优先进入老生代,当页被访问,才进入新生代,以解决预读失效的问题。
  • 页被访问时,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰的问题

猜你喜欢

转载自blog.csdn.net/BBinChina/article/details/123679776