【MySQL】四、MySQL的Buffer Pool介绍(一)
本文主要介绍MySQL数据库的Buffer Pool
4.1 回顾一下Buffer Pool在数据库里的地位
Buffer Pool是一个非常关键的组件,因为数据库中的数据实际上最终都是要存放在磁盘文件上的,但是我们在对数据库执行增删改操作的时候,不可能直接更新磁盘上的数据,因为如果直接对磁盘进行随机读写操作,那速度是相当的慢,随便一个大磁盘文件的随机读写操作,可能要几百毫秒。如果要是这么搞的话,可能你的数据库每秒也就只能处理几百个请求了。
在对数据库执行增删改操作的时候,实际上主要都是针对内存里的buffer pool中的数据进行的,也就是你实际上主要是对数据库内存里的数据结构进行了增删改。
4.2 Buffer Pool这个内存数据结构到底长什么样子?
4.2.1 如何配置buffer pool的大小
因为buffer pool本质其实就是数据库的一个内存组件,也就是一片内存数据结构,所以这个内存数据结构肯定是有一定的大小的,不可能是无限大的。
这个buffer pool默认情况下是128MB,还是有一点偏小了,我们实际生产环境下完全可以对buffer pool进行调整,比如我们数据库如果是16核32G的机器,那么可以给buffer pool分配个2GB的内存,使用下面的配置就可以:
1 | [server] |
我们先看一下下面的图,里面就画了数据库中的buffer pool内存组件:
4.2.2 数据页:MySQL中抽象出来的数据单位
现在假设我们的数据库中一定有一片内存区域是Buffer Pool了,那么我们的数据是如何放在Buffer Pool中的?
我们知道数据库的核心数据模型就是表+字段+行的概念,也就是说我们都知道数据库里有一个一个的表,一个表有很多字段,然后一个表里有很多行数据,每行数据都有自己的字段值。
实际上MySQL对数据抽象出来了一个数据页的概念。他是把很多行数据放在了一个数据页里,也就是说我们的磁盘文件中就是会有很多的数据页,每一页数据里放了很多航数据,如下图所示:
所以实际上假设我们要更新一行数据,此时数据库会找到这行数据所在的数据页,然后从磁盘文件里把这行数据所在的数据页直接加载到Buffer Pool里去,也就是说Buffer Pool中存放的是一个一个的数据页。如下图所示:
4.2.3 磁盘上的数据页和Buffer Pool中的缓存页是如何对应起来的
实际上默认情况下,磁盘中存放的数据页大小是16KB,也就是说,一页数据包含了16KB的内容。
而Buffer Pool中存放的一个一个的数据页,我们通常叫做缓存页,因为Buffer Pool是一个缓冲池,里面的数据都是从磁盘缓存到内存中去的。
而Buffer Pool中默认情况下,一个缓存页的大小和磁盘上一个数据页大小是一一对应起来的,都是16KB。
4.2.4 缓存页对应的描述信息是什么?
对于缓存页,实际上有一个描述信息,用来描述这个缓存页的,比如这个数据页所属的表空间、数据页的编号、缓存页在Buffer Pool中的地址以及别的一些东西。
每个缓存页都会有一个描述信息,这个描述信息本身也是一块数据,在Buffer Pool中,每个缓存页的描述数据放在最前面,然后各个缓存页放在后面。
Buffer Pool实际上看起来大概是下面这样子:
而且我们需要注意一点,buffer pool中的描述数据大概相当于缓存页大小的5%左右,也就是每个描述数据大小大概是800个字节左右的大小,然后假设设置的buffer pool大小是128MB,实际上Buffer pool真正的最终大小会超出一些,可能有130MB的样子,因为他里面还要存放每个缓存页的描述数据。
4.3 从磁盘读取数据页到Buffer Pool的时候,free链表有什么用
4.3.1 数据库启动的时候,如何初始化Buffer Pool的
数据库只要一启动,就会按照设置的Buffer Pool大小,稍微再加大一点,去操作系统申请一块内存区域,作为Buffer Pool的内存区域。
当内存区域申请完毕以后,数据库就会按照默认的缓存页的16KB的大小以及对应的800个字节左右的描述数据的大小,在Buffer Pool中划分出来一个一个的缓存页和一个一个的他们对应的描述数据。然后当数据库把Buffer Pool划分完毕之后,看起来就是上面那张图中展示的那样了。
只不过这个时候,Buffer Pool中的一个一个的缓存页都是空的,里面什么都没有,要等数据库运行起来后,当我们要对数据执行增删改操作的时候,才会把数据对应的页从磁盘文件里读取出来,放入Buffer Pool中的缓存页中。
4.3.2 怎么知道哪些缓存页是空闲的呢?
当数据库运行起来后,肯定会不停的执行增删改查的操作,此时就需要不停的从磁盘上读取一个一个的数据页放入Buffer Pool中对应的缓存页里去,把数据缓存起来,那么以后就可以对这个数据在内存里执行增删改查了。
从磁盘上读取数据页放入Buffer Pool中的缓存页的时候,就需要判断哪些缓存页是空闲的。默认情况下磁盘上的数据页和缓存页是一一对应起来的,都是16KB,一个数据页对应一个缓存页。所以我们必须要知道Buffer Pool中哪些缓存页是空闲的状态。
所以数据库为Buffer Pool设计一个free链表,它是一个双向链表数据结构,这个free链表里,每个节点就是一个空闲的缓存页的描述数据块的地址,也就是说,只要有一个缓存页是空闲的,那么他的描述数据块就会被放入这个free链表中。
刚开始数据库启动的时候,可能所有的缓存页都是空闲的,因为此时可能是一个空的数据库,一条数据都没有,所以此时所有缓存页的描述数据块,都会被放入这个free链表中。
可以看到上面出现了一个free链表,这个free链表里面就是各个缓存页的描述数据块,只要缓存页是空闲的,那么他们对应的描述数据块就会加入到这个free链表中,每个节点都会双向链接自己的前后节点,组成一个双向链表。
除此之外,这个free链表有一个基础节点,他会引用链表的头结点和尾节点,里面还存储了链表中有多少个描述数据块的节点,也就是多少个空闲的缓存页。
4.3.3 free链表占用多少内存空间?
这个free链表,他本身其实就是由buffer pool里面的描述数据块组成的,可以认为是每个描述数据块里面都有两个指针,一个是free_pre,一个是free_next,分别指向自己的上一个free链表节点以及下一个free链表的节点。
通过Buffer Pool中的描述数据块的free_pre和free_next两个指针,就可以把所有的描述数据块串成一个free链表。
对于free链表而言,只有一个基础节点是不属于Buffer Pool的,他是40字节大小的一个节点,里面就存放了free链表的头结点的地址,尾节点的地址,还有free链表里当前有多少个节点。
4.3.4 如何将磁盘上的页读取到Buffer Pool的缓存页中去?
当需要把磁盘上的数据页读取到Buffer Pool中的缓存页里去的时候,是怎么做到的呢?
首先我们需要从free链表里获取一个描述数据块,然后就可以对应的获取到这个描述数据块对应的空闲缓存页。
接着我们就可以把磁盘上的数据页读取到对应的缓存页里去,同时把相关的一些描述数据写入缓存页的描述数据块里去,比如这个数据页所属的表空间之类的信息,最后把那个描述数据块从free链表里移除就可以了。
4.3.5 怎么知道数据页有没有被缓存的?
我们在执行增删改查的时候,肯定是先看看这个数据页有没有被缓存,如果没有被缓存就走上面的逻辑,从free链表中找到一个空闲的缓存页,从磁盘中读取数据页写入缓存页,写入描述数据,从free链表中移除这个描述数据块。
但是如何数据页已经被缓存了,那么就会直接使用了。所以其实数据库还会有一个哈希表数据结构,他会用表空间号+数据页号,作为一个key,然后缓存页的地址作为value。
当你要使用一个数据页的时候,通过”表空间号+数据页号”作为key去这个哈希表里面查一下,如果没有就读取数据页,如果已经有了,就说明数据页已经被缓存了。
也就是说每次读取一个数据页到缓存之后,都会在这个哈希表中写入一个key-value对,key就是表空间号+数据页号,value就是缓存页的地址,那么下次如果再使用这个数据页,就可以从哈希表里面直接读取出来他已经被放入一个缓存页了。
【MySQL】四、MySQL的Buffer Pool介绍(一)