02、MySQL的Buffer Pool原理

数据库增删改查说起

回顾Buffer Pool

  • 存在原因: 对数据库读、写是随机IO操作,是相当慢的。
  • 引入解决的问题: 对数据的增删改主要放在内存中执行,增加TPS。
  • 为了防止宕机数据丢失,还引入了redo log日志,没刷盘的时候丢失数据,系统重启也能从redo log中加载之前的修改日志。

总结Buffer Pool:

  • 增删改实际自行地,配合redo log 、刷盘等机制和操作
  • 其是一个内存组件,缓存了真实的数据

Buffer Pool的内存结构:

配置Buffer Pool的大小
  • 默认情况下是128M大小,偏小

  • 如若是16C 32G机器,可分2G给Buffer Pool,设置参数为

    innodb_buffer_pool_size = 2G

数据是如何放入Buffer Pool中的?
  • 数据库的核心模型: 表 + 库 + 行
  • MySQL对数据抽离成一个数据页的概念 ,把很多行数据放一个数据页中。
  • 实际上要更新一行数据,此时会找到所在的数据页,然后从磁盘文件把数据页加载到Buffer Pool中
磁盘上数据页和Buffer Pool的数据页是如何对应的?
  • 磁盘上数据页大小为16K
  • Buffer Pool中默认情况下,一个缓存页的大小和磁盘上的一个数据页的大小是一一对应起来的,都是16KB
缓存页对应的描述信息?
  • 每个缓存页都有一个描述信息与之对应,用来描述缓存页
  • 包含:页所属表空间、页编号、页地址以及其他信息
  • 描述信息也是一块数据,在Buffer Pool的最前端
  • Buffer Pool描述信息大小大概相当于缓存页大小的5%左右
Buffer Pool初始化
  • 数据库启动,即会按Buffer Pool大小稍大一些,去操作系统中请求一块内存区域
  • 按16K的大小以及对应800字节的描述数据的大小,在Buffer Pool中划分出一个个缓存页和描述信息页
  • 运行起来后,才会逐渐将数据写入缓存页和描述信息页
MySQL怎么知道哪些页是空闲的?
  • 数据库会为Buffer Pool设计一个free链表,是一个双向链表结构,首尾相连
  • 每个节点就是一个空闲的缓存页的描述数据地址
  • free链表还有一个基础节点,会引用链表的头、尾节点,还存储了链表中有多少个描述数据块的节点,也是有多少空闲页就有多少个基础节点。
如何判断那些数据页是否被缓存
  • 数据库有一个哈希表数据结构,他会用表空间号+数据页号,作为一个key,然后缓存页的地址作为value。
  • 当要使用一个数据页的时候,通过“表空间号+数据页号”作为key去这个哈希表里查一下,如果没有就读取数据页,如果已经有了,就说明数据页已经被缓存了。

Buffer Pool常见问题

Buffer Pool中会不会有内存碎片?

  • 划分完所有页(描述数据页和缓存页)后,还会剩余一丁点内存空间,此时这个内存空间无法再放下任何一个页。
  • 那怎么减少内存碎片呢?描述数据和缓存页是挨着的,中间不会存在内存空间,回收的时候也是整页的回收、整页的加载。
数据为什么会脏?
  • 要更新的数据首先都会先加载到缓存页之中。
  • 更新完缓存页数据,提交完redo 日志后即会提交事务,此时还未刷盘更新操作。
哪些缓存页是脏数据?
  • 不可能所有缓存页都被刷入盘中,因为有一些数据未被修改,有一些是读加载进来的数据。
  • 有一个和free表一样的flush链表结构,里面记录了被修改后的描述数据页的地址,也为双向链表结构。
  • 但凡是被修改的数据,都会加入flush表中,LRU的时候才会进行刷盘(flush链表是如何用来记载脏数据页的)

Buffer Pool的LRU ?

如果Buffer Pool中缓存页不够了怎么办?
  • CRUD后都会将数据放入Buffer Pool之中。

  • 加载时,必然先加载到空闲页,直到内存结构满了。

  • 淘汰页: 将缓存被修改过的数据刷盘,同时清空,变成一个空闲的缓存页。

  • 缓存命中率概念:

    假设100个请求打到MySQL,缓存页A中命中了30个,但是缓存页B一个都未命中,故A的命中率是高命中率

引入LRU判断哪些缓存页是不常用的?
  • least Recently userd 即最近最少使用的数据。
  • 只要加载页,都会将他们的描述数据放入LRU链表头部,原本某数据在LRU链表尾部,只要查询则会将其放入到链表头部中去。
表、行、列与表空间、数据页的关系?
  • 列、行、表是逻辑概念
  • 表空间、数据页是物理概念
  • 一个表数据都是放在一个表空间中,由一堆磁盘文件组成;这些数据文件存放了你表里的数据
预读带来一巨大问题,LRU运行时存在隐患。
  • MySQL预读机制: 当你从磁盘加载一数据页时,可能连带相邻页也加载进来。
  • 实际上相邻页可能没人进行访问,但是它也被加载进了LRU链表头部
  • 此时,如若需要LRU,由于预读机制将热数据往后推了,导致热数据被LRU掉
哪些情况下会触发预读机制?
  • innodb_read_ahead_threshold ,默认为56,意思是如果顺序访问一个区的多个页,页超过此阈值,会触发预读机制,把下一个相邻区中所有页都加入缓存

  • 如果缓存了一个区的13个连续页,且这13个页被频繁访问,此时会触发预读机制,将该区的其他页都加载进缓存中去。

    innodb_random_read_ahead控制,默认是OFF关闭的。

全表扫描导致的被频繁访问的缓存页淘汰场景

如select * from user 会将表里所有数据都加载到缓存页中

为什么要存在预读机制?

提升性能,如分页查询的时候,会按顺序进行读取.

基于冷、热数据分离的LRU链表
  • 解决问题: 预加载数据将热数据顶下去的问题。

  • 真正LRU链表被拆分成两,由innodb_old_blocks_pct参数控制,默认是37,即冷数据占总缓存页的37%

  • 缓存页第一次被加载的时候,是放入冷数据链的头部

  • innodb_old_blocks_time,默认是1000毫秒,即在数据被加入冷链头部的时候,如果1000毫秒后,有请求访问此数据,则该数据会被放至热数据链头部

预读机制以及全表扫码加载进来的一大堆缓存页?
  • 都是存放在冷链之中,跟热链没有关系
  • 全表扫描仅有一次的情况下,是不会将缓存页从冷链数据区转移到热链数据区
  • 如果此时缓存页满了,需要淘汰一些缓存页,会首先从LRU链表的冷区域中的尾部去淘汰
热数据的优化
  • 只有在热数据的后3/4部分被访问到的时候,才会将其移动到热数据链表头部
  • 在前1/4部分数据被访问的情况下,是不会被移动的
定时把LRU链表冷数据区域尾部的部分缓存页刷盘

刷盘并清空页,加入free表中,从flush表中移除,从LRU链表中移除

Buffer Pool访问时加锁

先加锁,然后让一个线程先完成一系列操作,然后释放锁

多线程并发访问时加锁,对数据库性能的影响?

不会差:加锁和锁释放都是在内存中发生,基本都是微秒级的

生产环境的LRU优化

多个Buffer Pool如何优化并发能力?
  • 默认规则为: 如果Buffer Pool只给1G,则系统默认只给一个Buffer Pool

  • 如果为32G机器,可以给Buffer Pool 8G内存,则可以如以下设置:

    ​ [ server ] innodb_buffer_pool_size = 8G

    ​ innodb_buffer_pool_instances = 4

    ​ 上面设置为,将8G的Buffer Pool空间,每个为2G

  • 上述这套理论是无法进行动态实现Buffer Pool扩容的

基于chunk机制,把Buffer Pool拆分
  • 实际上,一个Buffer Pool是由很多chunk组成,大小由innodb_buffer_pool_chunk_size,默认每个chunk大小为128M
  • 每个chunk里就是一系列描述数据块和缓存页,多个chunk共享一套free表,flush表和LRU表
基于chunk如何支持运行期动态调整大小 ?
  • 只需要向操作系统申请一系列的128M的chunk页
  • 将申请的chunk分配给Buffer Pool进行管理
  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值