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之后访问这个缓存页,他才会被挪动到热数据链表的头部

解决之前的问题

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

相关推荐
spring2997922 小时前
MySQL无法连接到本地localhost的解决办法2024.11.8
数据库·mysql·adb
L1624762 小时前
MySQL 8.0 MGR + KeepAlived 生产级全流程搭建手册
数据库·mysql
Rust语言中文社区3 小时前
【Rust日报】用 Rust 重写的 Turso 是一个更好的 SQLite 吗?
开发语言·数据库·后端·rust·sqlite
Dontla3 小时前
VScode插件SQLite Viewer介绍(允许开发者不离开编辑器,直接打开、浏览和查询SQLite数据库文件)(ChromaDB、向量库插件、数据库插件、.sqlite3)DBeaver
数据库·vscode
星辰徐哥3 小时前
易语言数据库操作初步:内置Ado引擎与SQLite3快速上手
数据库·oracle·sqlite·易语言
守候秋林辉3 小时前
JFinal+SQLite 解决Date类型与DATETIME类型转换异常
jvm·数据库·sqlite
qq_416018723 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
逐鹿艾缇4 小时前
【达梦数据库】锁超时
数据库