MySQL高手第三章

从磁盘读取数据页到Buffer Pool的时候,free链表有什么用?

我们怎么知道那些缓存是空闲的?

当我们数据库运行起来的时候,肯定会不断的做增删改查,将磁盘上读取一个一个数据页放入Buffer Pool中对应的缓存页里去

但是从磁盘数据页放入Buffer Pool中的缓存页的时候,必然涉及到一个问题,那些缓存页是空闲的?

数据库会设计一个free链表,一个双向链表,这个free链表里,每个节点就是一个空闲的缓存页描述数据块的地址,只要你一个缓存页是空闲的,那么他的描述数据块就会放入这个free链表中

free链表中占用了多少内存空间?

这个free链表,他本身就是由Buffer Pool里的描述数据块组成的,可以认为是每个描述数据块里都有两个指针,一个是free_pre,一个是free_next

对于free链表而言,只有一个基础节点是不属于Buffer Pool的,他是40字节大小的一个节点,里面存放着链表头节点的地址,尾节点的地址,还有free链表里当前有多少个节点

如何将磁盘上的页读取到Buffer Pool缓存页中去?

只要将free链表里获取一个描述数据块,然后就可以对应的获取足够描述数据块对应的空闲缓存页

怎么知道数据页有没有被缓存?

数据库有一个哈希表数据结构,他会用表空间号+数据页号,作为一个key,然后缓存页的地址作为value

当你使用一个数据页的时候,通过"表空间号+数据页号"作为这个key去这个哈希表里检查一下,如果没有就读取数据页,如果有就代表存在

当我们更新Buffer Pool数据时,flush链表有什么用?

脏数据页为什么脏?

我们要更新的数据页都会在Buffer Pool的缓存页里,共我们在内存执行增删改操作

接下来去更新Buffer Pool缓存页中的数据,此时一旦更新了缓存页中的数据,那么缓存页里的数据和磁盘上的数据页里的数据,就会不一致,这个就叫做脏数据,脏页

哪些缓存页是脏页?

我们之前学过,最终在内存里更新的脏页的数据,都是要被刷新回磁盘文件的

但是不可能所有缓存页都刷回磁盘的,因为有的缓存页可能因为查询的时候被读取到Buffer Pool里去,可能没有被修改

所有数据库在这里需要引入和free表类似的flush链表,其本质就是通过缓存页的数据块中的两个指针,让被修改的缓存页的数据块,组成一个双向链表

凡是被修改的缓存页,都会把他的描述数据块加入到flush链表中,flush意思是脏数据,后续都要刷新flush到磁盘

当Buffer Pool的缓冲页不够的时候,如何基于LRU算法淘汰部分内存?

当我们执行CRUD的时候,无论是查询数据还是修改数据,实际上都会把磁盘的数据页加载到缓存页里来。

那么在加载数据到缓存页的时候,必然是要加载到空闲的缓存页中,所有必须要从free链表中找一个空闲的缓存页,然后把磁盘上的缓存页加载到那个空闲的缓存页里去。

随着不停把磁盘上的数据页加载到空闲的缓存页中,free链表中的空闲缓存页会越来越少,迟早有一刻缓存页会满

这时候还要加载一个空闲的缓存页该怎么办?

针对这个问题,我们就需要使用淘汰缓存页了

缓存命中率概念引入

假设现在有两个缓存页,一个缓存页的数据,经常被修改和查询,比如命中100次请求,有30次请求都在查询和修改这个缓存页中的数据,这样就是这个缓存命中率高

另外一个,查询和修改在100次请求中,就命中1个,那么其命中率就很低

现在如果我们要腾出空间来选择一个,我们肯定会选择第二种方式

LRU算法

我们怎么知道哪些缓存经常被访问,哪些缓存很少被访问?

这时候就需要LRU链表,也就是最近最少使用的意思

简单的LRU算法会遇到哪些问题?

预读机制

首先会带来隐患的是MySQL的预读机制,当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页也加载到缓存当中

哪些情况下会触发MySQL的预读机制?

  1. 有一个参数innodb_read_ahead_threshold,默认是56,如果顺序访问一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区域中的所有数据页都加载到缓存里面去
  2. 如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁被访问的,此时就会发送预读机制,把这个区域中的数据加到缓存中,这个机制是由innodb_random_read_ahead来控制的,默认是OFF,也就是关闭的意思

另外一种可能导致频繁被访问的缓存页被淘汰的场景

全表扫描,会导致一下子将这个表所有的数据页都从磁盘加载到Buffer Pool里面去,这时候会导致以前经常访问的缓存页都被淘汰了

MySQL是如何基于冷热数据分离方案,来优化LRU算法的?

冷热数据分离

前面我们说明了简单的LRU算法带来的问题,而MySQL肯定也考虑到了这一点,所以他们使用的是冷热数据分离的思想

真正的LRU链表是拆分为两部分,一部分是热数据,一部分是冷数据,这个比例是由innodb_old_blocks_pct参数控制的,默认是37%,也就是冷数据占37%

运行流程

第一次加载一个缓存页的时候,其会先放到冷数据链表的头部

MySQL里面设定了一个参数innodb_old_blocks_time,默认为1000,也就是说一个数据页被加载到缓存页后,1s之后访问这个缓存页,他才会被挪动到热数据链表的头部

解决之前的问题

前面我们说到全部扫描,这时候不就迎刃而解,当全表扫描的时候,会将其全部放到冷数据头部的位置,而不影响热数据

相关推荐
亚空间仓鼠21 小时前
关系型数据库MySQL(五):Galara高可用
数据库·mysql
weixin_5860614621 小时前
JavaScript中Redux-Thunk处理异步Action的任务流
jvm·数据库·python
C^h21 小时前
rtthread控制达妙4310电机
数据库·单片机·嵌入式硬件
晴天¥21 小时前
达梦数据库共享存储集群搭建(DSC双节点+Openfiler-IP SAN存储)
linux·数据库·达梦数据库
2401_897190551 天前
Golang怎么写TODO待办应用_Golang TODO应用教程【深入】
jvm·数据库·python
渔舟小调1 天前
P11 | 收藏与行程:用户行为类接口的设计模式
数据库·设计模式·oracle
m0_678485451 天前
CSS实现浮动图标与文本居中对齐_配合浮动与flex
jvm·数据库·python
做时间的朋友。1 天前
MySQL 8.0 窗口函数
android·数据库·mysql
试试勇气1 天前
MySQL--库的操作
数据库·mysql
2401_887724501 天前
uni-app动画效果实现 uni-app如何使用animation API
jvm·数据库·python