Mysql 专栏 – 缓冲池的内部结构(一)
前言
这一节咱们来介绍缓冲池的内部结构。如果不分明缓冲池是什么货色能够查看之前系列的第一篇文章。缓冲池最简略的了解为数据库磁盘文件在内存对应的映射,是一个非常重要的外围组件,缓冲池的内容和细节还是挺多的,这部分内容集体会限度篇幅让读者更好的消化。
缓冲池的介绍:Mysql 专栏 – mysql、innodb 存储引擎、binlog 的工作流程 #缓冲池
概述
-
Buffer pool 的内部结构
- 数据页和缓存页的关系
- 数据页的形容信息是什么?
- Mysql 是如何晓得哪些数据页被加载了
-
脏页
- 脏页的伪代码实现以及脏页的介绍
-
重点
-
分分明 free list 和 flush list
相熟整个 buffer pool 的结构图。
-
Buffer pool 构造
对于 buffer pool 的残缺结构图如下,本文将会一一合成来讲述各个块的内容:
Buffer pool 在 mysql 中位置
Buffer pool 能够看作是一个内存构造的组件,能够了解为一大片的内存区域,在默认的状况下它是 128m 的空间大小。须要留神的是默认值比拟小,通常状况下这个值是远远不够的。
从结构图来看缓冲池是十分外围的一个组件,因为 mysql 数据的操作不可能放到磁盘实现 的,哪怕是固态硬盘也是不可能快过内存,缓冲池能够看作是数据操作的时候磁盘文件的数据的一对一映射,然而如果咱们操作内存又会呈现另一个问题,内存的操作是非常快的,然而硬盘的刷新速度更不上内存,所以就会呈现内存和硬盘上的数据不统一的问题,种因为某些操作更新之后的内容更新过的数据页在 mysql 当中统称为脏页。
所以 redo log、undo log、bin log 这几个日志文件某种程度上能够认为是为了确保数据正确同步的策略而呈现的。
数据页和缓存页
既然缓冲池是一块内存空间,那么数据是否在 buffer pool 中呢?咱们的数据是如何放在 buffer pool 中的?
这里咱们咱们来回顾下数据库的 逻辑构造 ,数据库分为 表 + 字段 + 行 的模式,一个表有很多行数据,那么数据页的内容就是多行?其实数据库抽取了一个叫做数据页的概念,多行数据会放到一个数据页外部,磁盘中有多个页,每一个页都有很多行数据合并到一起,最终咱们更新数据就是找到某一个页的某一行。
数据页的默认大小:16kb,缓存页的大小也是 16kb。
总结:buffer pool 寄存的是一个个数据页,也叫做缓存页,因为 buffer pool 是一个近似内存的缓冲池,所以硬盘的数据会转变为一个个缓存页缓存到这块“内存”当中。Buffer pool 中一个缓存页和磁盘的一个数据页大小是对应的都是 16kb。
缓存页的形容信息
尽管咱们晓得了缓存页的大小,缓存页外面寄存的也是一行行的数据,然而缓存页本人是不晓得这些信息的,这时候 mysql 引入了另一个数据块叫做 缓存页的形容信息,在缓存页形容信息蕴含了上面的内容:
- 所属表空间
- 数据页编号
- 缓存页在缓冲池外面的所属地址
形容信息有多大呢?一个形容信息大略是 缓存页的 5% 左右 的大小,可能是 800 个字节 左右的大小,默认 128m 的缓冲池。须要留神的是,为了避免数据页撑满缓冲池导致形容信息无奈寄存,mysql 会给形容信息一些额定的内存空间保障形容信息能够记录所有的数据页(缓存页)。所以这外面128M 不是齐全固定的,会额定多个几 M 的缓存页形容信息。
形容信息如何寄存?
形容信息和缓存页依照相似“对称”的构造进行存储,形容信息放在缓存页的最后面,缓存页则放在缓冲池的最初面,至于这样设计的起因一方面是尽可能让形容信息不烦扰数据页的调配,另一方面是为了让缓冲池有“额定”并且足够的空间来寄存形容信息。
如何尽量减少 buffer pool 的内存碎片?
当缓存页和形容信息划分完数据块之后,必定是会存在一小部分的空间是既不能调配形容信息,又不能放下缓存块的内容的,所以这部分内容没有方法应用。
如何缩小呢?
在划分缓存页的时候让他们依照程序的排列程序严密排列,尽可能减少节约,其实就是程序的分配内存。
如何晓得哪些缓存页是闲暇的?
那么 mysql 是如何晓得缓存页是闲暇的呢?在 Buffer pool 会有一个叫做 free list 双向链表,链表每一个节点就是 数据页对应的形容数据块 。也就是说一个数据页是闲暇的,就会放到 free 链表中,并且在数据库刚刚启动 free 链表寄存了所有形容信息块内容。另外,如果相熟链表的构造,就会晓得链表当中会有一个 根底节点 (其实就是链表的头指针,只不过内容丰盛很多)来存储 开始节点 和完结节点 等内容。
至于这个根底节点的更新操作,相熟链表的人此时肯定非常分明了,其实就是双向链表的插入操作和删除操作。
为了更好的了解下面一大段的内容,咱们通过一个图来蕴含下面介绍的所有内容:
freelist 占用多少内存空间?
Buffer pool 和 freelist 外面的数据内容是截然不同的一份拷贝么?大错特错!因为形容信息在 freelist 外面是依据链表的节点规定串联的,同时因为这个节点只须要找到闲暇缓存块即可(Free List 中的所有节点都会指向一个从未被应用过的缓存页,说白了就是每个节点有个指向闲暇缓存页的一对一指针)。
free list 依据链表的定义规定,每一个形容信息都有两个指针,一个是 free_pre(前置节点),另一个是free_next(后继节点)。代表了一个双向链表的 node,通过这两个指针就把所有的形容数据串联成一个 free 链表,根底的 node 节点自身占用了 40 个字节,寄存 头节点和尾节点的地址 ,以及free 链表外面以后有多少个节点 和其余的信息。
如何将磁盘的页读到 buffer pool 的缓存页?
如何把磁盘的页读到 buffer pool?咱们有了 free list 之后就能够办到了,能够从 free 链表获取一个形容数据块,接着能够通过这些形容信息找到对应的数据页读到缓冲池外面去,最初再把闲暇列表的 node 去除即可。
如何移除节点?
其实只有通过双向链表的根底节点外面的头尾指针找到节点并且把前置节点或者后置节点的对应援用设置为 null 即可。也就是相熟的双向链表的删除操作。
怎么晓得数据是否真的进来了?
理解了数据页如何加载到缓冲池,接下来咱们来看下 mysql 怎么晓得哪一个数据页加载到缓冲池,个别的流程必定是当申请进来的时候先查看缓冲池有没有数据,如果没有缓存页就须要先去 free list 找一下这个数据页的形容信息,而后再通过磁盘文件把这个数据页加载到缓冲池,而后再把 free list 对应的形容信息节点删除掉。
数据页缓存哈希表的构造是什么?
如果数据页被缓存了会间接查缓存,那么缓冲池怎么晓得申请是要找它呢?其实 Mysql 还有一种构造是 哈希表 的构造,这个构造能够看作是一个 Map,key 保留的是表空间 + 数据页。而 value 则是缓存页的地址。当申请执行器调用接口的时候,就会依据哈希表找到对应的缓存页,如果没有缓存页就去 freelist 找到这块数据页加载进来。
脏页
什么是脏页?
当缓冲池的数据被更新,然而磁盘的数据和缓存页的内容不统一的时候,能够说这个页是一个“脏”页。
mysql 怎么晓得哪些页是脏页
那么 mysql 是应用 free list 列表进行确认的?这么做必定不行,因为这个链表是寄存的有哪些数据页没有被加载,并不能晓得哪些缓存页是脏页。所以这时候 mysql 又引入了一个链表,叫做 flush list,它的构造和 free list 相似也是一个双向链表,同样有一个根底节点保护整个链表的的信息,然而和 free list 不同的是它所存储的是脏页的形容信息而不是所有的数据页形容信息。(同样也是在每一个节点存在一个指针指向对应的缓存页)
提醒:如果还记得本系列的第一篇(前言地址)文章中简略的提到了 io 线程定期把缓存页刷新到磁盘文件中如何找到脏页的?其实就是通过这个双向链表来实现的,然而刷新的动作是 随机刷新。此问题在后续的文章中会再次提到。
最初对于 flush list 的结构图放到了本文的结尾。
flushlist 和 freelist 伪代码
上面通过两个图来剖析一下两个链表的伪代码结构图,对于具体的解释都放到了代码的正文当中,这里就不多啰嗦了:
上面是引入第二个节点之后的内容:
思考题:
逻辑构造和物理构造
咱们在 SQL 语句里都是用到的是表和行的概念,然而之前我 们提到的表空间、数据页,他们之间的 关系 是什么呢?
表和行是逻辑的存储构造。
然而数据页,表空间,都是 物理的存储构造。实际上表的数据都是放在一个表空间,表空间由数据文件(数据块)组成,数据文件存储的是一行行的数据,所以能够认为整个 mysql 磁盘文件形象的了解为是由一个个数据块组成的。
物理构造和逻辑构造的区别就是他们的本质区别。
小结:
看完这篇文章置信大家有点晕了,mysql 外部两个链表和一个哈希表甚至后续还会有更多的链表来保护信息,这些内容很容易混同,所以最初再回顾后面提到的结构图来帮忙读者进行回顾:
写在最初
缓冲池的内容看似简单,然而参考结构图了解话其实套路是差不多的,本文的内容难度不会很难,不过到了后续随着缓冲池的细节难度会逐步加大,最初对于内容编排有任何谬误的中央欢送指出。