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外部两个链表和一个哈希表甚至后续还会有更多的链表来保护信息,这些内容很容易混同,所以最初再回顾后面提到的结构图来帮忙读者进行回顾:
写在最初
缓冲池的内容看似简单,然而参考结构图了解话其实套路是差不多的,本文的内容难度不会很难,不过到了后续随着缓冲池的细节难度会逐步加大,最初对于内容编排有任何谬误的中央欢送指出。