(1)数据库启动时如何初始化Buffer Pool?
    数据库一启动,就会按照你设置的Buffer Pool的大小,稍微加大一点,去找操作系统申请一块内存区域,作为Buffer Pool的内存区域。然后当内存区域申请完毕之后,数据库就会按照 默认缓存页的16KB大小以及对应的800个字节左右的描述数据的大小,在Buffer Pool中划分出来一个一个缓存页和一个一个对应的描述数据。
    只不过这个时候,Buffer Pool的一个一个的缓存页是 空的,里面什么都没有,要等数据库运行起来之后,当我们要对数据执行增删改查的时候,才会把数据对应的页从磁盘文件里读取出来,放到Buffer Pool的缓存页中。

    (2)怎样知道哪些缓存页是空闲的呢?
    这是关键问题,从磁盘上读取数据页放入Buffer Pool中的缓存页的时候,必然会涉及到一个问题,哪些缓存页是空闲的?数据库为Buffer Pool设计了一个free链表,这是一个双向 链表,每个节点就是空闲的缓存页的描述数据块的地址,也就是说,只要一个缓存页是空闲的,那么他的描述数据块就会在free链表中。(free链表 空闲的描述数据块地址组成的链表
    刚开始数据库启动的时候,可能所有的缓存页都是空闲的, 所以此时缓存页的所有描述数据块都在Free链表中。Free链表是个双向链表所以它的每个节点都存放了他前后节点的地址。那么这个Free链表放在哪里了呢?实际上free链表就是描述数据块组成的, 每个描述数据块本身就是free链表里的节点,每个描述数据块都有两个指针指向他的前后节点。
    对于Free链表而言,只有一个基础节点是不属于Buffer Pool的,它是一个40字节大小的节点,里面存放了free链表的头节点的地址,尾节点地址还有free链表里面当前有多少个节点。
    12.png

    (3)如何将磁盘上的页读取到Buffer Pool的缓存页中?
    有了free链表就简单了,我们从free链表里获取一个描述数据块,然后就可以对应的获取到这个描述数据块对应的空闲缓存页,接着我们就可以把磁盘上的数据页读取到对应的缓存页里去 ,同时把相关的一些描述数据写入缓存页的描述数据块里去。比如这个数据页所属的表空间之类的信息,最后把那个描述数据块从free链表里删除,即改变该节点的指针就可以,例如设为null。 这样就删除了。
    (根据空闲描述数据块获取到空闲缓存页然后将数据页加载到缓存页)

    (4)怎样知道数据页有没有被缓存呢?
    如何知道数据页有没有已经加载到Buffer Pool中?其实数据库还有一个哈希表结构,他用 表空间号+数据页号 作为key,缓存页地址作为value,当你使用一个数据页的时候,通过 “表空间号+数据页号”作为key去哈希表里查一下,value为空就读取数据页,不为空就说明数据页已经被缓存,每次读取不到缓存页时都会将这个数据页加入缓存以key-value的形式。下次再去 读取直接从哈希表里读取就可以了。(表空间号+数据页号为key,缓存页地址作为value,组成hash表,使用数据页前查一下hash表结构是否有value
    13.png

    问题:Buffer Pool中会不会有内存碎片?
    肯定是有的,Buffer Pool大小是通过参数配置的,很可能Buffer Pool划分完全部的缓存页和描述数据块之后,还剩一点内存,这一点内存放不下任何一个缓存页了,所以这点内存就 只能放着不用,这就是内存碎片,
    减少内存碎片的方式:在Buffer Pool划分缓存页的时候,让所有的缓存页和描述数据块都紧密的挨在一起,尽可能的减少内存浪费,这样就可以尽可能的减少内存碎片的产生。