Mysql成神之路----InnoDB 的 Buffer Pool

411 阅读5分钟

缓存的重要性

我们只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以进行读写访问了,在进行完读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省去磁盘IO的开销了。这样能够大大的增加效率。

InnoDB的Buffer Pool

Buffer Pool的概念

设计InnoDB的大叔为了缓存磁盘中的页,在MySQL服务器启动的时候就向操作系统申请了一片连续的内存,他们给这片内存起了个名,叫做Buffer Pool(中文名是缓冲池)。那它有多大呢?这个其实看我们机器的配置,如果你是土豪,你有512G内存,你分配个几百G作为Buffer Pool也可以啊,当然你要是没那么有钱,设置小点也行呀~ 默认情况下Buffer Pool只有128M大小。当然如果你嫌弃这个128M太大或者太小,可以在启动服务器的时候配置innodb_buffer_pool_size参数的值,它表示Buffer Pool的大小,就像这样: [server] innodb_buffer_pool_size = 268435456 其中,268435456的单位是字节,也就是我指定Buffer Pool的大小为256M。需要注意的是,Buffer Pool也不能太小,最小值为5M(当小于该值时会自动设置成5M)。

Buffer Pool内部结构

Buffer Pool中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是16KB。为了更好的管理这些在Buffer Pool中的缓存页,设计InnoDB的大叔为每一个缓存页都创建了一些所谓的控制信息,这些控制信息包括该页所属的表空间编号、页号、缓存页在Buffer Pool中的地址、链表节点信息、一些锁信息以及LSN信息。 每个缓存页对应的控制信息占用的内存大小是相同的,我们就把每个页对应的控制信息占用的一块内存称为一个控制块吧,控制块和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边,所以整个Buffer Pool对应的内存空间看起来就是这样的:

每一个控制块都对应一个缓存页,那在分配足够多的控制块和缓存页后,可能剩余的那点儿空间不够一对控制块和缓存页的大小,自然就用不到喽,这个用不到的那点儿内存空间就被称为碎片了。当然,如果你把Buffer Pool的大小设置的刚刚好的话,也可能不会产生碎片~

free链表的管理

因为我们没法得意buffer pool的那些内存被使用了,也没有办法哪些是空闲的能够被使用的,所以我们用控制块来管理缓存页,控制块里有一个free链表来管理缓存页的使用情况。 有了这个free链表之后事儿就好办了,每当需要从磁盘中加载一个页到Buffer Pool中时,就从free链表中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上(就是该页所在的表空间、页号之类的信息),然后把该缓存页对应的free链表节点从链表中移除,表示该缓存页已经被使用了~

缓存页的哈希处理

我们如何去访问某个数据呢,如果数据加载到buffer pool中,我们怎么才能准确的知道,这个内容是不是已经加载到了beffer pool中呢,是这样的,我们其实是通过表空间号+页号当作key通过hash算法来定位,缓存页就是一个value,通过hash表的方式,去寻址如果有的话就直接使用,如果没有的话,我们就把数据从磁盘中加载进来。

flush链表的管理

如果beffer pool其中某一页的数据被修改,那么她就和磁盘中的数据不一致了,我们给这种数据一个状态,我们称它是dirty,当然最简单的方法是一被修改我们立刻刷新到磁盘中保持高度的一致性,但是频繁的刷盘会影响性能,所以一般情况下,我们都是到某一个时间点或者某一个条件然后去刷盘的。那么我们如何才能快速的定位到需要刷盘的数据呢,也就是我们所说的dirty page。其实缓存页一被修改我们就把放到了一个专门放dirty page的链表中,我们称它为flush链表,当需要刷盘的时候一起进行刷盘。

LRU链表的管理

其实beffer pool其实也不是无限大的,最后肯定会有满的时候,那么我们应该怎么去删除数据让新的数据加载进来呢,答案肯定是最近使用频率低肯定要首先淘汰,下面要说的肯定就是著名的lru淘汰算法,还有一定要注意的就是如果我们淘汰一个数据的时候一定要确定它是否是dirty page如果是的话那么就不能淘汰因为还没有同步到磁盘,这样的话就操成数据丢失。