MySQL专题(三):细节梳理InnoDB存储引擎中的Buffer pool缓冲区核心组件 数据库是如何初始化buffer pool 三大链表 如何通过多个buffer pool优化数据块并发性能

前言:

这篇文章会比较长,专门针对InnoDB核心组件buffer pool展开分析《BUFFER POOL》
一:细节梳理InnoDB存储引擎中的Buffer pool缓冲区核心组件

1.如何设置buffer pool内存大小
2.数据页:MySQL中抽象出来的数据单位
3.磁盘上的数据页是如何与buffer pool中的缓存页对应的
4.缓存页对应的描述信息是什么?

二:数据库启动的时候,是如何初始化buffer pool
三:buffer pool中的三大链表

1.free链表
2.flush链表
3.LRU链表【MySQL预读机制针对普通的LRU链表会引发的问题和MySQL对于LRU链表的优化处理细节(MySQL是如何基于冷热数据分离的方案,来优化LRU算法的?)以及MySQL的预读机制和全表扫描的机制对原生的LRU链表产生的影响】

四:如何通过多个buffer pool来优化数据库的并发性能?

1.buffer pool在访问的时候需要加锁吗?
2.多线程并发访问加锁,数据块的性能还能好吗?
3.多个buffer pool优化并发能力
4.如何基于机器配置来合理设置buffer pool?
5.如何通过chunk来支持数据块运行期间的buffer pool动态调整?

五:MySQL数据库启动之后,可以通过show engine innodb status命令查看innodb的一些情况

1:细节梳理InnoDB存储引擎中的Buffer pool缓冲区核心组件

1.1:如何设置buffer pool内存大小

buffer pool默认的缓冲池大小是128MB,其实了解完数据页之后,会发现128MB实际上页不小,小型并发等等管理系统一般来说够用了。

1.2:数据页:MySQL中抽象出来的数据单位

实际上在buffer
pool上面都是一个个的数据页,一个数据页包含了多行数据,还包含了数据页头等等信息,这里为了方便理解,打断一下文章次序,上个图:
Bufferpool中存放的缓存数据页
假设需要更新一条一行数据,数据库会先找到这行数据所在的数据页,从磁盘文件中将这行数据所在的数据页加载到buffer pool中,再开始进行修改等等操作,这也就解开的之前两篇文章的数据页疑惑

1.3:磁盘上的数据页是如何与buffer pool中的缓存页对应的?

磁盘文件中存放的数据页大小是16KB,一页数据包含了16KB的内存,buffer pool中存放的是一个个数据页,buffer pool默认的情况下,一个缓存页的大小和磁盘上的一个数据页大小是对应的,都是16KB,buffer pool中的数据页一般称为【缓存页】

1.4:缓存页对应的描述信息是什么?
(每个缓存页都有一个描述信息),描述信息可以理解为用来描述这个缓存页的,每个缓存页都会对应一个描述信息,这个描述信息本身也是一块数据,在buffer pool中,每个缓存页的描述数据放在最前面,缓存页放在后面。大体看上去就是这样的【如图】
缓存页对应的描述信息

二:数据库启动的时候,是如何初始化buffer pool的

总共分为几个步骤:

1.数据库只要一启动就会按照设置/默认的buffer pool+稍微大点的大小,去找OS操作系统申请一块内存区域作为buffer pool。

2.当buffer pool内存区域申请完毕之后,MySQL数据库就会按照默认的缓存页16KB大小以及对应的800字节左右的描述数据的大小,在bufferpool中划分出来一个个的缓存页和一个个的描述数据块。

3.但是此时buffer pool中的一个个缓存页都是空的,什么都没有,需要等待数据库运行起来之后,对数据执行CRUD操作的时候,才会将数据对应的数据页从磁盘中读取出来放入buffer pool的缓存页。

4.数据库运行起来之后,需要不停的从磁盘上读取,一个个的数据页放入buffer pool中对应的缓存页,将数据页缓存,【从磁盘读取数据页放入buffer pool中的缓存页,如何知道哪些缓存页是空闲的呢?(天知道,我不知道,此时就引出第一个链表的概念free链表)通俗易懂的讲就是:数据库会为buffer pool设计一个free链表,数据结构是一个双向链表数据结构,free链表中,每个节点就是一个缓存页的描述数据块地址,也就是说,只要一个缓存页是空闲的,那么这个缓存页对应的描述数据库就会被放入这个free链表中】详细解释下这个free链表,顺带画个简图:当数据库要加载数据页到buffer pool的时候,先会从free链表中获取一个描述数据块,这样就可以获取到对应的空闲缓存页,接着将磁盘上的数据页读取到对应的缓存页中去,同时将相关的描述数据写入缓存页的描述数据块中去,最后将描述数据块从free链表中去除就完成了

5.那么问题来了,如何判断一个缓存页是否已经被缓存呢?

如果要加载的数据页已经被缓存了,那么就会直接使用了,MySQL数据库还会有一个哈希表数据结构Map结构的【表空间号+数据页号】,表空间号+数据页号会作为key,缓存页的location作为value,当要使用这个数据页的时候,通过“表空间号+数据页号”作为key去这个hash表中查询一下,如果没有找到,就读取到数据页,如果有就说明数据页已经被缓存了。

三:buffer pool中的三大链表

1.free链表

文章中的第二大点的第4小点已经对free链表做了一个详细的解释,这里还是继续提一嘴,加深印象:也就是说当从磁盘加载数据页到buffer pool中的时候,此时会先从free链表中去hash一下,key就是表空间号+数据页号,如果获取到了,那么就代表数据页已经被缓存了,可以直接使用buffer pool中的缓存页了,如果没有获取到,加载的时候需要有空闲的缓存页,也就是会去找free链表要个空闲页去加数据页到buffer pool。

2.flush链表

MySQL数据库引入了另外一个跟free链表类似的flush链表,这个flush链表本质也是通过缓存页的描述数据块中的两个指针,让被修改过的缓存页的描述数据块,组成一个双向链表【也就是说,凡是被修改过的缓存页,都会将他的描述数据块加入到flush链表中去,flush的意思就是这些脏页,后续都是要flush刷新到磁盘上去的】,讲官话一点:就是在buffer pool对某个缓存页进行操作了之后,就会将这个缓存页对应的描述数据块加载到flush链表中,后续MySQL的IO线程进行刷盘的时候,也是基于flush链表的。

3.LRU链表

问题:buffer pool中的缓存空间是有限制的,不可能将所有disk上的数据页全部加载到缓存页,LRU链表实际上和redis的热冷数据LRU清除算法是差不多概念的,最后也是MySQL也是基于热冷块对LRU算法进行优化改良的。

【LRU算法的本质(前世今生)】
首先有几个问题:
3.1:如果buffer pool中的缓存不够了怎么办?

LRU替你解决困扰

3.2: 淘汰掉一些缓存数据,淘汰谁?

当然是淘汰比较少用到的缓存页

3.3: 缓存命中率的概念

A缓存页平均100次请求,就要被命中20次,B缓存页几乎不被命中,淘汰谁?

3.4: LRU的工作原理(基本工作原理)

假设某个缓存页的描述数据本来在LRU链表的尾部,后续只要查询或者修改了这个数据页的数据,也要将这个缓存页移动到LRU链表的头部去,也就是最近被访问过的缓存页一定在LRU链表的头部。末尾淘汰制

3.5:3.4的末尾淘汰制的可能和MySQL的全表扫描或者是MySQL的预读机制产生的奇异问题:

3.5.1:全表扫描,例如select * from users;可能扫出来N++条数据,一个数据页只有16KB,那么一个此时可能需要加载N++个数据页到buffer pool中,但是实际用到的可能只是其中的某几个数据页,但是可能出现的奇异就是,可能这几个在使用的第一次加载进来的时候正好排在LRU的尾部,因为加载了N++的数据页,可能就会导致buffer pool空间的不足,那么此时这些被使用的,反倒被强行刷盘了,尬了个尴!

3.5.2:MySQL有一个预读机制:简单的讲一下,就是如果连续读取了多个连续的数据页,MySQL会自动将之后的可能5-10个数据页,只是打比方,会加载到buffer pool中,这样可以提升效率,下一次就不用再次加载数据页到buffer pool了,但是可能也会出现全表扫描的类似问题,这些预读的在前,用的在后,用的被淘汰了!

3.6:解决LRU链表的尴尬问题:《MySQL是如何基于冷热数据分离的方案,来优化LRU算法的?》
3.6.1:基于冷热数据分离的思想设计LRU链表;

为了解决简单LRU链表的纹理,真正MySQL在设计LRU链表的时候,采用的实际上是冷热数据分离的思想,真正的LRU链表,会被拆分成两个部分已不是是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,默认是37%,也就是冷数据占比37%。

3.6.2:数据页第一次被加载到缓存的时候做的事情;

LRU链表已经按照一定的比例被拆分成冷热两块数据,第一次被加载到缓存的时候,实际上缓存页会被放在冷数据区域的链表头部。

3.6.3:冷数据区域的缓存页什么时候会被放入到热数据区域?

innodb_old_blocks_time参数,默认值是:1000ms,也就是说一个数据页被加载到缓存页之后,在1s之后,再次访问的这个缓存页,才会被移动到热数据区域的链表头部去。【如图】
在这里插入图片描述

四:如何通过多个buffer pool来优化数据库的并发性能?

1.buffer pool在访问的时候需要加锁吗?

多线程访问一个buffer pool,必然是需要加锁的,先让一个线程先完成一系列的操作(加载数据到缓存页,更新free链表,更新LRU链表,然后释放锁)

2.多线程并发访问加锁,数据块的性能还能好吗?

多线程并发buffer pool是必然会加锁的,多个线程会串行的执行排队,执行操作,【但是即使是一个buffer pool,多线程会加锁,性能也差不到多少,因为大部分的情况下,每个线程crud都是发生在内存中的,基本都是微秒级的,包括更新free,flush,lru这些链表,都是基于链表进行一些指针操作的,性能也是极高的】

3.多个buffer pool优化并发能力

可以通过这个参数来设置多个buffer pool
设置buffer pool缓冲区大小:innodb_buffer_pool_size = 8589934592
设置多个buffer pool :innodb_buffer_pool_instances = 4
以上的配置意思就是给buffer pool设置了8GB的总内存,设置了4个buffer pool,也就是相当于每个buffer pool的内存为2GB,此时就会分散并发的请求。

4.如何基于机器配置来合理设置buffer pool?

一般设置为机器的50%-60%为buffer pool的总大小。例如MySQL服务器64G,分配给30-35G就行。

5.如何通过chunk来支持数据块运行期间的buffer pool动态调整?

MySQL中设计了一个chunk机制,也就是说buffer pool是由很多chunk组成的大小是innodb_buffer_pool_chunk_size参数控制的,默认值就是128MB,举例:例如给buffer pool设置一个总大小为8GB,如何设置4个buffer pool,那么每个buffer pool就是2GB,此时每个buffer pool是由一系列的128MB 的chunk组成的,也就是说buffer pool会有16个chunk。每个buffer pool中的每个chunk中就是一系列的描述数据和缓存页,每个buffer pool中的多个chunk共享一套free,fulsh,lru这些链表,如果需要不停机动态修改buffer pool,那么只需要申请一系列的128MB大小的chunk就可以了,只要每个chunk是连续的128MB内存就可以了,然后将这些申请到的chunk内存分配给buffer pool,《个人觉得了解下就行,也就是说是支持动态修改buffer pool缓冲区大小的》

五:MySQL数据库启动之后,可以通过show engine innodb status命令查看innodb的一些情况;

切记,我的sqlyog直接输入命令执行看不到,cmd命令窗口才能看到,可以尝试一下【如图】
buffer pool cmd窗口查看buffer pool相关信息
最后再上一个小结图:【buffer pool的整体结构图】
buffer pool的整体结构图

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱ABC

无需打赏,共同进步学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值