MySQL InnoDB Buffer Pool

news/2024/7/6 6:20:16

1 缓存的重要性

我们知道,对于使用 InnoDB 作为存储引擎的表来说,不管是用于存储用户数据的索引(包括聚簇索引和二级索引),还是各种系统数据,都是以页的形式存放在表空间中的,而所谓的表空间只不过是 InnoDB 对文件系统上一个或几个实际文件的抽象,也就是说我们的数据说到底还是存储在磁盘上的。

但是磁盘的速度慢,所以 InnoDB 存储引擎在处理客户端的请求时,当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中,也就是说即使我们只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以进行读写访问了,在进行完读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省去磁盘 IO 的开销了。

2 Buffer Pool

InnoDB 为了缓存磁盘中的页,在 MySQL 服务器启动的时候就向操作系统申请了一片连续的内存,他们给这片内存起了个名,叫做 Buffer Pool(中文名是缓冲池)。那它有多大呢?这个其实看我们机器的配置,默认情况下 Buffer Pool只有 128M 大小,这个值其实是偏小的。

#134217728 = 128 * 1024 * 1024
show variables like 'innodb_buffer_pool_size';

在这里插入图片描述

Buffer Pool 中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是16KB。为了更好的管理这些在 Buffer Pool 中的缓存页,InnoDB 为每一个缓存页都创建了一些所谓的控制信息,这些控制信息包括该页所属的表空间编号、页号、缓存页在 Buffer Pool 中的地址。

每个缓存页对应的控制信息占用的内存大小是相同的,我们称为控制块。控制块和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边,所以整个 BufferPool 对应的内存空间看起来就是这样的。

在这里插入图片描述

2.1 Free List

最初启动 MySQL 服务器的时候,需要完成对 Buffer Pool 的初始化过程,就是先向操作系统申请 Buffer Pool 的内存空间,然后把它划分成若干对控制块和缓存页。但是此时并没有真实的磁盘页被缓存到 Buffer Pool 中(因为还没有用到),之后随着程序的运行,会不断的有磁盘上的页被缓存到 Buffer Pool 中。

那么问题来了,从磁盘上读取一个页到 Buffer Pool 中的时候该放到哪个缓存页的位置呢?或者说怎么区分 Buffer Pool 中哪些缓存页是空闲的,哪些已经被使用了呢?最好在某个地方记录一下 Buffer Pool 中哪些缓存页是可用的,这个时候缓存页对应的控制块就派上大用场了,我们可以把所有空闲的缓存页对应的控制块作为一个节点放到一个链表中,这个链表也可以被称作 free 链表(或者说空闲链表)。刚刚完成初始化的 Buffer Pool 中所有的缓存页都是空闲的,所以每一个缓存页对应的控制块都会被加入到 free 链表中,假设该 Buffer Pool 中可容纳的缓存页数量为 n,那增加了 free 链表的效果图就是这样的:

在这里插入图片描述
有了这个 free 链表之后,每当需要从磁盘中加载一个页到 Buffer Pool 中时,就从 free 链表中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上(就是该页所在的表空间、页号之类的信息),然后把该缓存页对应的 free链表节点从链表中移除,表示该缓存页已经被使用了。

2.2 Flush List

如果我们修改了 Buffer Pool 中某个缓存页的数据,那它就和磁盘上的页不一致了,这样的缓存页也被称为脏页(英文名:dirty page)。当然,最简单的做法就是每发生一次修改就立即同步到磁盘上对应的页上,但是频繁的往磁盘中写数据会严重的影响程序的性能。所以每次修改缓存页后,我们并不着急立即把修改同步到磁盘上,而是在未来的某个时间点进行同步。

所以,需要再创建一个存储脏页的链表,凡是修改过的缓存页对应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的,所以也叫 flush 链表。链表的构造和 free 链表差不多。

在这里插入图片描述

2.3 LRU List

Buffer Pool 对应的内存大小毕竟是有限的,如果需要缓存的页占用的内存大小超过了 Buffer Pool 大小,也就是 free 链表中已经没有多余的空闲缓存页的时候该咋办?当然是把某些旧的缓存页从 Buffer Pool 中移除,然后再把新的页放进来,那么问题来了,移除哪些缓存页呢?

再创建一个链表,由于这个链表是为了按照最近最少使用的原则去淘汰缓存页的,所以这个链表可以被称为 LRU 链表(LRU 的英文全称:Least Recently Used)。当我们需要访问某个页时,可以这样处理 LRU 链表:

如果该页不在 Buffer Pool 中,在把该页从磁盘加载到 Buffer Pool 中的缓存页时,就把该缓存页对应的控制块作为节点塞到 LRU 链表的头部。如果该页已经缓存在 Buffer Pool 中,则直接把该页对应的控制块移动到 LRU链表的头部。

也就是说:只要我们使用到某个缓存页,就把该缓存页调整到 LRU 链表的头部,这样 LRU 链表尾部就是最近最少使用的缓存页。所以当 Buffer Pool 中的空闲缓存页使用完时,到 LRU 链表的尾部找些缓存页淘汰就行了。


http://www.niftyadmin.cn/n/4556529.html

相关文章

一个C++编程问题

fibonacci数列: 1 1 2 3 5 8 13 21 34 55 89 ... 即f(1)f(2)1 f(n)f(n-1)f(n-2) n > 2

MySQL MVCC底层原理解析

1 事务并发中遇到的问题 1.1 脏读 当一个事务读取到了另外一个事务修改但未提交的数据,被称为脏读。 1.2 不可重复读 当事务内相同的记录被检索两次,且两次得到的结果不同时,此现象称为不可重复读。 1.3 幻读 当一个事务同样的查询条件…

怎么办 学的云里雾里的 C语言

||| 多看看书呗 仔细看看~~学后面的知识要及时巩固前面的 就有点感觉啦 ||| 楼住我觉的你该多看看书 我觉得不难啊应付考试的话就那100题就好了 ||| 多用软件测试每个函数的作用. ||| 把教材从头到尾 在错误中寻找答案 ||| 多编程 这样印象就比较深刻了 多看些习题 再就是自己要…

java方法的概念,方法重载,参数传递

一.方法的语法格式 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,......){ 执行语句……… return 返回值; } 修饰符:方法的修饰符比较多,有对访问权限进行限定的&a…

C语言的免费教学软件在哪可以下载

http://download.csdn.net/source/471768这里可以下载 答案补充 http://www.skycn.com/soft/17869.html ||| http://www.bccn.net/shipin/Special/erjicyuyan/Index.html 免费视频教程

给我传一个吧。急用 谁有VC++6.0简体中文版的开发工具 谢谢拉

cidC7AE9D00A3F2468722240859F8B467245141C230&t2&fmt-绝对可以的 cid7497C46991962334EBE62CD8A838BA92479CBF11&t2&fmt-1&redirectno ||| http://58.251.57.206/down searchVC%2B%2B6.0%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87&id0 vc6.0简体中文版htt…

Linux 后台运行命令:nohup 和

【参开文章】:nohup 与 & 的区别 1. nohup 1.1 基本概念 将程序以忽略挂起信号的方式运行起来; 不可以免疫 Ctrl C 的 SIGINT 中断信号; 可以免疫 SIGHUP 的 挂断信号; 测试: 查看启动脚本,两个…

.net C#连接数据库例子

表我建 ||| 最简单的连接SQL的方法: SqlConnection conn new SqlConnection("server.;databasemybase;uidsa;pwdsa;"); SqlConnection conn new SqlConnection("server./mySqlServer;databasemybase;uidsa;pwdsa;"); SqlConnection conn new…