参考:《InnoDB Buffer Pool 管理调研》by 何登成
一、LRU list
LRU List 按照功能被划分为两部分:LRU_young 与 LRU_old,默认情况下, LRU_old 为链表长度的 3/8。页面读取时(get/read ahead),首先链入 LRU_old 的头部。页面第一次访问(read/write),从 LRU_old 链表移动到 LRU_young的头部(整个 LRU 链表头)。这种策略可将预读的页先保存于LRU_old中,因为预读的页不一定会被访问,所以不应当直接放到LRU最前面。
1、old page to new:page 每次被访问时,都会调用 buf0buf.cc::buf_page_set_accessed_make_young 函数,进一步调用buf_page_peek_if_too_old。
2、new page became old:第一次读入buf;随着访问,LRU_young中的部分页退化为LRU_old;通过 LRU list flush 的 dirty page,被移动到 LRU 链表的尾部,可以立即释放,交给用户线程分配 free page。
二、关于buf_LRU_get_free_block
当用户需要读取一个在外存中的页面,或者是分配一个新的页面进行插入,就需要调用该函数进行页面的分配。
1、尝试从 Buffer Pool 的 free list 中分配新页面。
2、若free list 中不存在空闲页面,遍历 LRU 链表,寻找其中的非脏页面,将这些页面从 LRU 链表中摘除,并插入 free list 。
3、若不能成功释放一个clean page,从 LRU 链表中 flush 一些 dirty page至磁盘,并将flush后的clean page移到LRU链表的最后(也是LRU_old的最后),然后释放 执行2 。(由 buf_flush_free_margin 完成)
备注:在Mysql5.6.2之后,InnoDB 在后台新增了一个 page cleaner 线程,专门用于处理 dirty page 的 flush (包括 LRU list 与 flush list)。buf_flush_free_margin的主要操作已经移至该线程中。
三、Flush list
补充:free 类型的 page,一定位于 buf pool 的 free 链表中。clean,dirty 两种类型的 page,一定位于 buf pool 的 LRU 链表中;dirty page 还位于 buf pool 的 flush 链表中。
flush list 中的 dirty page,按照 page的 oldest_modificattion 时间排序,oldest_modification 越大,说明 page 修改的时间越晚,就排在 flush 链表的头部;oldest_modification 越小,说明 page 修改的时间越早,就排在 flush链表的尾部。当 InnoDB 进行 flush list 的 flush 操作时,从 flush list 链表的尾部开始,写出足够数量的 dirty pages,推进 Checkpoint 点,保证系统的恢复时间。
add page to flush list:页面访问/ 修改都被封装为一个 mini-transaction , 当mini-transactin 提交的时候,也就是该 mini-transaction 修改的页面进入 flsuh list 的时候。(mtr_commit -> mtr_memo_note_modification())
remove page from flush list:一是 LRU list flush;二是 Flush list flush。(在Mysql5.6.2之后,都移至page cleaner线程中。)
四、Buffer Pool LRU/Flush List flush 对比
总结来说,Flush lish flush 与 LRU list flush 有以下几个不同之处:
1. LRU list flush,由用户线程触发(MySQL 5.6.2 之前);而 Flush list flush 由 InnoDB 后台srv_master 线程处理。(在 MySQL 5.6.2 之后,都被迁移到 page cleaner 线程中)
2. LRU list flush,其目的是为了写出 LRU 链表尾部的 dirty page,释放足够的 free pages,当 buf pool 满的时候, 用户可以立即获得空闲页面,而不需要长时间等待; Flush list flush,其目的是推进 Checkpoint LSN,使得 InnoDB 系统崩溃之后能够快速的恢复。
3. LRU list flush, 其写出的 dirty page,需要移动到 LRU 链表的尾部(MySQL 5.6.2 之前版本);或者是直接从 LRU 链表中删除,移动到 free list(MySQL 5.6.2 之后版本)。 Flush list flush,不需要移动 page 在 LRU 链表中的位置。
4. LRU list flush,由于可能是用户线程发起, 已经持有其他的 page latch,因此在 LRU list flush中,不允许等待持有新的 page latch,以防导致 latch 死锁;而 Flush list flush 由后台线程发起,未持有任何其他 page latch,因此可以在 flush 时等待 page latch。
5. LRU list flush, 每次 flush 的 dirty pages 数量较少,基本固定, 只要释放一定的 free pages即可; Flush list flush, 根据当前系统的更新繁忙程度,动态调整一次 flush 的 dirty pages数量,量更大。