Featured image of post InnoDB 缓冲池 Buffer Pool

InnoDB 缓冲池 Buffer Pool

🧊 Buffer Pool

MySQL 的数据是存储在磁盘中的,为了提升读写性能,InnoDB 设计了一个缓存池 Buffer Pool。

本质上是 InnoDB 向操作系统申请的一段连续内存空间,可以通过 innodb_buffer_pool_size 来进行调整大小。

  • 当读取数据时,如果数据已经在 Buffer Pool 中,会直接返回;如果不在,就会将数据完整的页信息全部加载到内存中,再返回给客户端。

  • 当修改数据时,会修改 Buffer Pool 中的缓存页(-> 变成脏页),在合适的时机,由后台线程写入到磁盘。

🧩 结构

Buffer Pool 中的连续内存,会被分割成一个个的页,和 InnoDB 的页大小一样,都是 16KB,称为缓存页。

每一个缓存页都会由一个控制块进行管理(位于 Buffer Pool 的最前面),每个缓存页与控制块一一对应。

🧩 内容

在 Buffer Pool 中,主要包括了六部分的内容:

  • 数据页
  • 索引页
  • 插入缓冲
  • undo 页
  • 自适应哈希索引
  • 锁信息

👷‍♂️ 管理 Buffer Pool 的链表

在 MySQL 服务运行过程中,连续的 BuuferPool 内存空间的页面有空闲的,也有非空闲的,为了能够快速的找到需要的页面,防止一个一个的遍历浪费时间, InnoDB 使用了许多🔗链表管理 Buffer Pool 的页(空闲页、干净页、脏页)。

⛓️ Free 链表 – 管理空闲页

💡 空闲页:Buffer Pool 内存中未被使用的页。

为了能快速找到空闲的缓存页,使用 Free 空闲链表管理空闲页。

Free 链表的节点是一个个空闲页所对应的控制块。

当需要从磁盘中加载一个页到 Buffer Pool 时,就从 Free 链表中找到一个空闲页,加载数据并更改控制块的信息,并从 Free 链表中移除。

⛓️ Flush 链表 – 管理脏页

💡 脏页(dirty page):修改了缓存页中的数据,与磁盘中的页数据不一致,称为脏页。

被修改过的缓存页的控制块,会被作为节点加入到 Flush 链表。

Flush 链表的结构和 Free 链表差不多。后台线程会在一定的时机,通过遍历 Flush 链表,将脏页写入磁盘(WAL)。

⛓️ LRU 链表 – 管理脏页&干净页

💡 干净页(clean page):加载了数据未作修改的缓存页。

💡 脏页(dirty page):加载了数据做了修改的缓存页。

Buffer Pool 的大小是有限的,不可能将所有的数据都放在内存中,innoDB 通过 LRU 算法(修改过的),将频繁访问的数据停留在 Buffer Pool 中。

简单的 LRU 算法在 MySQL 中可能会导致:

  • 😵 预读失效

    MySQL 会将被加载页的临近页一同加载到内存(为了减少磁盘 IO),如果这些页没有被访问,这个预读相当于浪费了。

    (通过将 LRU 链表划分为 young 区和 old 区,加载的数据现放到 old 区域,被访问到之后,才被加入 young 区域)

  • 🤢 Buffer Pool 污染

    一个 SQL 扫描大量的数据(如索引失效时,进行全表扫描来判断条件),由于链表空间有限,可能会把内存中的所有页全部替换出去,导致热点数据全部失效,后续缓存全部没有命中,导致大量磁盘 IO。

    (加大数据放入 young 区的条件,加载到 old 区域的数据,停留时间超过阈值时,才有资格放到 young 区域)

InnoDB 的 LRU 链表被分为两个区域:(1)热数据 young 区域;(2)冷数据 old 区域。

  • 📃 预读的页只会加载到 old 区域的头部,当该页真正被访问的时候,才会被移动到 young 区的头部。

  • ⏱️ 移动到 young 区域的条件除了被访问外,还需要判断停留在 old 区域的时间。(防止全表扫描时,被访问一次后就不在使用)。当访问的缓存页已经在 old 区域停留时间超过阈值 innodb_old_blocks_time=1000ms 时,才会被插入到 young 区头部。

💿 脏页刷盘的时机

当修改数据时,会记录对应的 redo log & bin log(WAL),修改的是 Buffer Pool 中的缓存页,然后将其设置为脏页并加入 Flush 链表,由后台线程刷新数据到磁盘。

以下情况会主动刷新数据到磁盘:

  • 🎗️ 当 redo log 日志写满时

  • 🎗️ 当 Buffer Pool 空间不足时,会淘汰一些缓存页,如果时脏页,会刷入磁盘

  • 🎗️ 当 MySQL 认为空闲时,异步地将脏页刷新到磁盘

  • 🎗️ 当 MySQL 主动正常关闭前

Licensed under CC BY-NC-SA 4.0