- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章面试官:能说一说MySQL缓存池吗?由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
大家好,我是狂聊君.
今天来聊一聊 Mysql 缓存池原理.
提纲附上,话不多说,直接干货.
前言 。
面试官:同学,你能说说Mysql 缓存池吗?
狂聊君:啊,这么难吗,容我组织一下语言。(内心OS:这TM还不简单?我能给你扯半小时!) 。
面试官:可以,给你一分钟时间想一想吧.
....一分钟后.... 。
狂聊君:我准备好了,你可听好,我要开始表演了.
为什么要有缓存池?
Mysql 的 innodb 存储引擎是基于磁盘存储的,并且是按照页的方式进行管理的.
在数据库系统中,CPU 速度与磁盘速度之间的差距是非常大的,为了最大可能的弥补之间的差距,提出了缓存池的概念.
所以缓存池,简单来说就是一块「内存区域」,通过内存的速度来弥补磁盘速度较慢,导致对数据库造成性能的影响.
缓存池的基本原理 。
「读操作」
在数据库中进行读取页的操作,首先把从磁盘读到的页存放在缓存池中,下一次读取相同的页时,首先判断该页是不是在缓存池中.
若在,称该页在缓存池中被命中,则直接读取该页,否则,还是去读取磁盘上的页.
「写操作」
对于数据库中页的修改操作,首先修改在缓存池中的页,然后在以一定的频率刷新到磁盘,并不是每次页发生改变就刷新回磁盘,而是通过 checkpoint 的机制把页刷新回磁盘.
可以看到,无论是读操作还是写操纵,都是对缓存池进行操作,而不是直接对磁盘进行操纵.
缓存池结构 。
Buffer Pool 是一片连续的内存空间,innodb 存储引擎是通过页的方式对这块内存进行管理的.
缓存池的结构如下图:
可以看到缓存池中包括数据页、索引页、插入缓存、自适应哈希索引、锁信息、数据字段.
其中数据页和索引页会用掉多数内存.
「但是,innodb 是如何管理缓存池中的这么多页呢?」 。
为了更好的管理这些缓存的页,innodb 为每一个缓存页都创建了一些所谓的控制信息,这些控制信息包括该页所属的:
每个缓存页对应的控制信息占用的内存大小是相同的,我们把每个页对应的控制信息占用的一块内存称为一个「控制块」.
「控制块」和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 的后边.
Buffer Pool 对应的内存空间示意图:
缓存池参数设置 。
缓存池管理 。
「管理缓存池依赖的链表结构」:
Free 链表 。
当启动 Mysql 服务器的时候,需要完成对 Buffer Pool 的初始化过程,即分配 Buffer Pool 的内存空间,把它划分为若干对控制块和缓存页,但是此时并没有真正的磁盘页被缓存到 Buffer Pool 中,之后随着程序的运行,会不断的有磁盘上的页被缓存到 Buffer Pool 中.
在使用过程中,为了记录哪些缓存页是可用的,我们把所有空闲的页包装成一个节点组成一个链表,这个链表可以称作为 Free 链表(空闲链表)。因为刚刚完成初始化的 Buffer Pool 中所有的缓存页都是空闲的,所以每一个缓存页都会被加入到 Free 链表中.
为了方便管理 Free 链表,特意为这个链表定义了一些「控制信息」,里面包含链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息.
另外会在每个 Free 链表的节点中都记录了某个「缓存页控制块」的地址,而每个「缓存页控制块」都记录着对应的「缓存页地址」,所以相当于每个 Free 链表节点都对应一个空闲的缓存页.
给大家画了个结构图:
这图怎么样,这下能看的懂了吧.
2、Lru 链表 。
Lru 链表用来管理已经读取的页,当数据库刚启动时,Lru 链表是空的,此时页也都放在 Free 列表中,当需要读取数据时,会从 Free 链表中申请一个页,把从放入到磁盘读取的数据放入到申请的页中,这个页的集合叫做 Lru 链表.
3、Flush 链表 。
Flush 链表用来管理被修改的页,Buffer Pool 中被修改的页也被称之为「脏页」,脏页既存在于 Lru 链表中,也存在于 Flush 链表中,Flush 链表中存的是一个指向 Lru 链表中具体数据的指针.
因此只有 Lru 链表中的页第一次被修改时,对应的指针才会存入到 Flush 中,若之后再修改这个页,则是直接更新 Lru 链表中的页对应的数据.
这三者之间是这么个关系:
读操作 。
Buffer Pool 一个最主要的功能是「加速读」。加速读是当需要访问一个数据页面的时候,如果这个页面已经在缓存池中,那么就不再需要访问磁盘,直接从缓冲池中就能获取这个页面的内容。当我们需要访问某个页中的数据时,就会把该页加载到 Buffer Pool 中,如果该页已经在 Buffer Pool 中的话直接使用就可以了.
问题:那么如何快速查找在 Buffer Pool 中的页呢?
为了避免查询数据页时扫描 Lru,其实是根据表空间号 + 页号来定位一个页的,也就相当于表空间号 + 页号是一个 key,缓存页就是对应的 value。用表空间号 + 页号作为 key,缓存页作为 value 创建一个哈希表,在需要访问某个页的数据时,先从哈希表中根据表空间号 + 页号看看有没有对应的缓存页.
如果有,直接使用该缓存页就好.
如果没有,那就从 Free 链表中选一个空闲的缓存页,然后把磁盘中对应的页加载到该缓存页的位置。每当需要从磁盘中加载一个页到 Buffer Pool 中时,就从 Free 链表中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上,然后把该缓存页对应的 Free 链表节点从链表中移除,表示该缓存页已经被使用了,并且把该页写入 Lru 链表.
在初始化的时候,Buffer pool 中所有的页都是空闲页,需要读数据时,就会从 Free 链表中申请页,但是物理内存不可能无限增大,数据库的数据却是在不停增大的,所以 Free 链表的页是会用完的.
因此需要考虑把已经缓存的页从 Buffer pool 中删除一部分,进而需要考虑如何删除及删除哪些已经缓存的页。假设一共访问了 n 次页,那么被访问的页在缓存中的次数除以 n 就是缓存命中率,缓存命中率越高,和磁盘的 IO 交互也就越少 .
为了提高缓存命中率,InnoDB 在传统 Lru 算法的基础上做了优化,解决了两个问题:1、预读失效 2、缓存池污染 。
写操作 。
Buffer pool 另一个主要的功能是「加速写」,即当需要修改一个页面的时候,先将这个页面在缓冲池中进行修改,记下相关的重做日志,这个页面的修改就算已经完成了.
被修改的页面真正刷新到磁盘,这个是后台刷新线程来完成的。前面页面更新是在缓存池中先进行的,那它就和磁盘上的页不一致了,这样的缓存页被称为脏页(dirty page).
问题:这些被修改的页面什么时候刷新到磁盘?以什么样的顺序刷新到磁盘?
最简单的做法就是每发生一次修改就立即同步到磁盘上对应的页上,但是频繁的往磁盘中写数据会严重的影响程序的性能。所以每次修改缓存页后,不能立即把修改同步到磁盘上,而是在未来的某个时间点进行同步,由后台刷新线程依次刷新到磁盘,实现修改落地到磁盘.
但是如果不立即同步到磁盘的话,那之后再同步的时候如何判断 Buffer Pool 中哪些页是脏页,哪些页从来没被修改过呢?
InnoDB 并没有一次性把所有的缓存页都同步到磁盘上,InnoDB 创建一个存储脏页的链表,凡是在 Lru 链表中被修改过的页都需要加入这个链表中,因为这个链表中的页都是需要被刷新到磁盘上的,所以这个链表也叫 Flush 链表,链表的构造和 Free 链表一致.
这里的脏页修改指的此页被加载进 Buffer Pool 后第一次被修改,只有第一次被修改时才需要加入 Flush 链表,对于已经存在在 Flush 链表中的页,如果这个页被再次修改就不会再放到 Flush 链表.
需要注意,脏页数据实际还在 Lru 链表中,而 Flush 链表中的脏页记录只是通过指针指向 Lru 链表中的脏页。并且在 Flush 链表中的脏页是根据 oldest_lsn(这个值表示这个页第一次被更改时的 lsn 号,对应值 oldest_modification,每个页头部记录)进行排序刷新到磁盘的,值越小表示要最先被刷新,避免数据不一致.
原文地址:https://mp.weixin.qq.com/s/Fk4uWxXQB3CgEBASESu41A 。
最后此篇关于面试官:能说一说MySQL缓存池吗?的文章就讲到这里了,如果你想了解更多关于面试官:能说一说MySQL缓存池吗?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
最近,我们将专用 SQL 池部署到生产中的 Synapse 工作区。在开发中,我们可以访问无服务器 SQL 池和专用 SQL 池。但是,在生产中,我们可以访问无服务器 SQL 池,但无法访问专用 SQ
假设您从一个项目公开 WCF 服务,并使用“添加服务引用”(在本例中为 Framework 3.5 WPF 应用程序)在另一个项目中使用它。 当您重新实例化 ClientBase 派生代理时,Clie
我有一个函数,它使用 multiprocessing.Pool 并行处理一个数据集中的所有数据。 from multiprocessing import Pool ... def func():
我正在尝试使用进程对象在 python 中使用工作池。每个 worker (一个进程)进行一些初始化(花费大量时间),传递一系列作业(理想情况下使用 map()),并返回一些东西。除此之外,不需要任何
我是软件工程师,最近我构建了我的 Linux 机器,想探索更多系统管理员类型的任务。我已经探索并阅读了很多关于 ZFS 的内容,但我越来越困惑,因为每篇文章对它的描述都不一样。 Everything
我有 zfs 池: $ sudo zpool status lxd pool: lxd state: ONLINE scan: none requested config: NAME
我有一个基于 Actor 的项目,对于其中的一部分,我必须使用一些接收消息的 Actor ,然后一个 Actor 分别分配给每个请求,每个 Actor 负责执行其消息请求,所以我需要类似线程的东西我的
我已经使用 QEMU 模拟器成功地将 FreeBSD 安装到原始图像文件中。我已经使用 ZFS 文件系统 (ZFS POOL) 格式化了图像文件。 使用下面的命令我已经成功地挂载了准备好由 zpool
我正在使用 multiprocessor.Pool并行处理一些文件。该代码等待接收文件,然后使用 Pool.apply_async 将该文件发送给工作人员。 ,然后处理文件。 这段代码应该一直在运行,
我正在使用带有光滑的 Bonecp 数据源。并发现池包含关闭的连接所以我总是遇到这个异常 java.sql.SQLException: Connection is closed! at com
我有apartment gem的 Multi-Tenancy Rails应用程序,我可以使用apartment-sidekiq在每个工作程序中成功切换数据库租户。但是,sidekiq worker 正
ZFS 池可能由数据集(文件系统、快照等)或卷组成。 ZFS 卷就像 block 设备,但我不明白池和文件系统之间的区别。当我通过 zpool create pool1 sda sdb sdc 创建
我在 docker 容器上运行了 airflow。我正在使用 airflow 2.0.2 版。 我知道我实际上可以通过 UI 创建池。但我正在寻找一种通过 pools.json 文件在 docker
我在tomcat中有一个jdbc池,用于建立数据库连接。我在使用后没有显式关闭连接对象。我的“maxActive”参数设置为100。应用程序运行了一段时间,但随后失败进行数据库查询。它会等待无限时间来
阅读 PostgreSQL 文档 here我读了以下内容: As well, connections requested for users other than the default config
我在 docker 容器上运行了 airflow。我正在使用 airflow 2.0.2 版。 我知道我实际上可以通过 UI 创建池。但我正在寻找一种通过 pools.json 文件在 docker
我正在读取一个大的 URL 文件并向服务发出请求。该请求由返回 ListenableFuture 的客户端执行。现在我想保留一个 ListenableFuture 池,例如最多同时执行 N 个 Fut
我想使用队列来保存结果,因为我希望消费者(串行而不是并行)在工作人员产生结果时处理工作人员的结果。 现在,我想知道为什么以下程序挂起。 import multiprocessing as mp imp
我正在开发一个单页应用程序,目前正在构建一个 JQuery、ajax 函数,以便我的所有调用都能通过。 对于一个典型的页面,我可能有 3 个 ajax 调用。我的想法是,如果用户互联网出去将这些 aj
我有一个单位类及其一些子类(弓箭手、剑客等)。我怎样才能创建一个回收所有单元类型子类的池? 最佳答案 这是不可能的,因为池只能包含一种特定类型的对象。否则你可能会遇到这样的情况: Pool unitP
我是一名优秀的程序员,十分优秀!