MySQL Innodb Buffer Pool

推荐阅读:MySQL 是怎样运行的:从根儿上理解 MySQL

Buffer Pool

InnoDB存储引擎中用于缓存表的数据和索引页的重要组件。
查看 Buffer Pool 的状态信息(所有信息包含Buffer Pool信息)

SHOW ENGINE INNODB STATUS;

大小配置

innodb_buffer_pool_size

可在MySQL配置文件中设置大小,默认单位是字节,也可以使用K、M、G等后缀表示大小。

innodb_buffer_pool_size = 1G

自 MySQL 5.7.5 版本之后,可以在服务器运行过程中使用SET GLOBAL命令调整 Buffer Pool 大小。但在生产环境中建议在低峰期进行此类操作以避免潜在的性能问题。

# eg:将buffer pool大小设置为2GB:
SET GLOBAL innodb_buffer_pool_size = 2G;
# 验证设置是否生效
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
注意点
  • 由于调整buffer pool大小,会影响到内存的分配和数据库的工作负载,可能会有短暂的性能波动。
  • 调整buffer pool大小不会影响现有连接,只会影响之后的新连接。
  • 不要设置过大的buffer pool,以免占用过多内存导致系统出现内存压力。

innodb_buffer_pool_chunk_size

每个 Buffer Pool 实例由若干个 chunk 组成,每个 chunk 的大小可以在服务器启动时通过启动参数调整。
默认128M,5.7,8.0版本

innodb_buffer_pool_chunk_size=134,217,728

InnoDB引擎会根据innodb_buffer_pool_size和系统的可用内存动态地划分buffer pool,每个块的大小由innodb_buffer_pool_chunk_size决定。
在一般情况下,不太需要手动设置innodb_buffer_pool_chunk_size,因为InnoDB会自动进行适当的调整。通常只需关注innodb_buffer_pool_size来确保整个buffer pool的大小符合系统的内存配置。

内部组成

整个Buffer Pool的内存分布如下,控制器在头部,缓存页在尾部:
buffer pool

控制器与缓存页

  • 信息包含 页所属表空间编号、页号、页在BufferPool中的地址、链表节点信息、锁信息、LSN信息
  • 控制器块约缓存页大小的5%
    MySQL5.7.21版本中,每个控制块占用的大小是808字节。可设置的innodb_buffer_pool_size并不包含这部分控制块占用的内存空间大小,也就是说InnoDB在为Buffer Pool向操作系统申请连续的内存空间时,这片连续的内存空间一般会比innodb_buffer_pool_size的值大5%左右。

三个主要的链表

每个链表都有基节点,40字节大小(并不包含在为 Buffer Pool 申请的一大片连续内存空间之内,而是单独申请的一块内存空间)

FREE链表(空间链表)

FREE链表
控制块里有前后指针,当BufferPool加载页时,就从空闲链表中去一个控制器块移除出来,并把页对应信息填入块中。

FLUSH链表

脏页控制器块节点构成的链表,结构与free链表相同。

规则
  • 首次修改 Buffer Pool 中的缓存页时,会将其对应的控制块插入到 flush链表 的头部;将修改该页面的mtr 开始时对应的 lsn 值写入控制器块的oldest_modification属性。
  • 再次修改时已在 flush 链表中,不再插入。只修改该页面的 mtr 结束时对应的 lsn 值写入控制器块的newest_modification属性。

flush链表中的脏页控制器块节点是按照页面的第一次修改时间从晚到早降序排序。

LRU链表

free链表耗尽,无空闲的缓存页时,通过此链表淘汰最近最少使用的页

简单的LRU链表
  • 首次加载的页,其控制器块节点插入链表头部
  • 已在链表中的页(已加载),放到头部
存在的问题:两类情况降低缓存命中率
  1. 预读(满足条件时触发异步 预先加载 之后可能需要的页 到Buffer Pool):

    • 线性预读(innodb_read_ahead_threshold,默认值是56):当在当前区顺序读取的页超过默认值时,InnoDB会触发异步预读下一个区的所有页到Buffer Pool。

      区(extent):64个16kb的页构成一个区,默认占用1M大小。这也限制了innodb_read_ahead_threshold的可选范围为0-64。

    • 随机预读(innodb_random_random_ahead,默认值为 OFF):读取数据页时,发现已有13个同区连续页存在于Buffer Pool中,则将该区剩余的页面都加载到Buffer Pool中。
  2. 全表扫描:短时间内访问大量使用频率非常低的页面

分区的LRU链表

通过冷热分区和访问间隔时间,解决预读与全表扫描引起的缓存命中率降低的问题。
分区的LRU链表
按照比例将LRU链表分成两半,节点不是固定在young或old区域的,随着程序的运行,节点所属的区域也可能发生变化

# 查看该比例
SHOW VARIABLES LIKE 'innodb_old_blocks_pct';
规则
  1. 页初次加载时,其对应控制器块放入old区头部;
  2. 对于位于old区的页进行第一次访问时,将访问时间点记录到控制器块中,后续再次访问时,若访问的时间间隔在系统Innodb_old_blocks_time(默认1000ms)内,则不移入young区
young区优化

热数据访问频繁导致频繁移动young区节点 =》优化策略:访问位于young区1/4后的位置,才移动到头部(1/4前的位置为非常热的位置)

运行机制

快速检测页是否已加载

使用哈希表:表空间编号+页号 =》缓存页地址
适应性哈希索引(Adaptive Hash Index)来加速对页的查找。这个哈希索引存储了buffer pool中各个页的引用,使得在内存中快速定位一个页

脏页刷盘

后台线程定时刷盘
  1. BUF_FLUSH_LRU:将LRU链表冷数据中的一部分刷盘;
  2. BUF_FLUSH_LIST:将FLUSH链表中的一部分刷盘;在系统不繁忙时,从尾部开始向头部扫描innodb_lru_scan_depth个控制器块,若有脏页则刷盘;
  3. BUF_FLUSH_SINGLE_PAGE:当后台线程刷盘慢,而用户线程要加载新页却无页可用时,用户线程会尝试释放LRU链表尾部控制器块对应的页,若为脏页则要同步刷盘,这将降低用户请求处理速度;
  4. 系统繁忙时,用户线程批量从FLUSH链表刷脏页入盘的清空,见于redo log的check point。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值