InnoDB 缓冲池 | 提升数据库性能的关键

silverwq
2022-11-15 / 0 评论 / 299 阅读 / 正在检测是否收录...

innodb 为了缓存磁盘中的页,在启动的时候,申请了一片连续的内存,这片内存叫做缓冲池。然后把缓冲池中连续的内存划分成 3 段,第小段是控制块、第二小段是碎片、第三小段是缓冲页。内存空间如下所示:

lagh4v2b.png

  1. 控制块:在缓冲池连续内存的开头,有很多个控制块,和缓冲页一一对应,大小大概是是缓冲页的 5%左右,用于存储该缓冲页的页号、以及在 buffer pool 中的地址、是否是脏页、是否是 change _buffer 等信息,可以理解成缓冲页的索引
  2. 碎片:每一个控制块都对应一个缓冲页,那么在分配足够多的控制块和缓冲页后,剩余的那点儿空间可能不够一对控制块和缓冲页的大小,自然也就用不到了。这个用不到的内存空间就称为碎片
  3. 缓冲页:把这块连续的内存,分成了若干个页,每一页的大小和表空间的页的大小一致,都是 16kb,缓冲池中的页我们称作缓冲页。

怎么区分换缓冲页中的哪些页是空闲的,然后把磁盘的页数据存到空闲的缓冲也中呢?

在系统启动的时候,所有的缓冲页都是空闲的,这个时候我们可以它们对应的控制块地址放到一个链表中,这个链表也可以称为free 链表(或者说空闲链表)。之后,每当需要从磁盘中加载一个页到 Buffer Pool 中时,就从 free 链表中取一个控制块,然后把表空间号和页号记录到控制块里,再把数据存到该控制块所对应的缓冲页中,并且把该控制块从链表中移除掉就好了。

那再次查询的时候,如何判断缓冲页中已经有该数据了呢?

由于把数据存储缓冲页的时候,已经把表空间号和页号记录到控制块,所以我们可以把为表空间号和页号作为 key,缓冲页控制块的地址作为值来创建一个哈希表。所以如果 key 存在的话,就可以根据控制块取出缓冲页的值。

什么是 change buffer?

如果是写数据,也是先从 buffer pool 里找,如果找到了就直接写,就变成了脏页,如果写是时候,在 buffer pool 没有找到,就会把写的数据记录在 change buffer 里,先不去查找 io,然后如果下一次要读这个数据时候,会到磁盘 io,然后和 change buffer 里的这个数据合并起来。

什么是脏页?

当我们修改了缓冲页中的数据,它就和磁盘中的不一致了,这样的缓冲页我们叫做脏页。

因为我们不能立即对脏页数据刷盘,那么之后要刷盘的时候,怎么知道哪些是脏页?

mysql 创建一个存储脏页的链表,凡事被修改过的缓冲页的控制块地址都会被放到这个链表中,因为这个链表节点对应的缓冲页都是需要被刷新到磁盘上的,所以也称为 flush 链表

因为红冲池的大小是固定的,如果大小不够的时候,该淘汰哪些红冲页的数据呢?

Buffer Pool 中不再有空闲的缓冲页时,就需要淘汰掉最近很少使用的部分缓冲页。我们可以创建一个 LRU 链表,当使用到某个页的时候,就把该页的控制块地址放到链表头部,如果需要淘汰的时候,只需要把链表尾部的缓冲页给淘汰掉就好了。同时,还需要将 lru 链表分为热数据和冷数据两个区,防止以下两种情况:

  1. mysql 的预读,可能读取到数据没有用,却放到了 lru 链表的头部,所以优化后要放到 old 区的头部
  2. 全表扫描,扫到的数据很多,把热数据从 lru 链表中挤到后面,被淘汰了,所以优化后要放到 old 区的头部

当然 lru 链表还有一些细节优化,这里没必要研究的太深。

其它链表?

mysql 主要是通过链表来管理终端的缓冲页,还有其它很多缓冲页,这里不赘述了。

如何刷新脏页到磁盘的呢?

mysql 中有一个后台线程,定时的去执行以下两个步骤:

  1. 从 LRU 链表的冷数据中刷新一部分页面到磁盘,可以从冷数据的控制块信息上查看是否是脏数据,是的话就刷盘
  2. 从 flush 链表中刷新一部分页面到磁盘,当然,如果要缓冲数据的时候,缓冲页中都是脏数据,那么用户线程就会将脏数据刷盘,这个尽量要避免。
0

评论 (0)

取消