IT技术博客大学习 共学习 共进步

MySQL数据库InnoDB存储引擎 Buffer Pool Flush List详解

MySQLOPS 数据库与运维自动化技术分享 2012-06-20 00:05:06 浏览 2,945 次

Buffer Pool Flush List

add page to flush list

buffer pool中的page,有三种状态:

l  free:      当前page未被使用

l  clean:    当前page被使用,对应于数据文件中的一个页面,但是页面未被修改

l  dirty:     当前page被使用,对应于数据文件中的一个页面,同时页面被修改

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点,保证系统的恢复时间。

那么,dirty page是在什么时候进入flush list的呢?看过我以前文档的同学,一定知道InnoDB存储引擎有一个所谓的mini-transaction,页面访问/修改都被封装为一个mini-transaction,当mini-transactin提交的时候,也就是该mini-transaction修改的页面进入flsuh list的时候。

mtr_commit -> mtr_memo_note_modification();

// 若当前page已经是dirty page,不是第一次修改,那么说明当前page已经在

// flush list之中,因此不需要再次加入flush list

if (block->page.oldest_modification)

ut_ad(block->page.oldest_modification <= mtr_start_lsn);

else

// 若当前page是第一次修改,oldest_modification = 0,则将page加入flush list

buf_flush_insert_into_flush_list();

buf_flush_list_mutex_enter(buf_pool);

remove page from flush list

有两种操作,可以将dirty page从flush list中移除。一是LRU list flush;二是Flush list flush。其中,LRU list flush已经在前面的章节中分析。

而Flush list flush,也就是我们通常所说的InnoDB fuzzy Checkpoint,可以参考我以前的一个文档:MySQL InnoDB Insert Buffer Checkpoint AIO实现分析

Buffer Pool LRU/Flush List flush对比

总结来说,Flush lish flush与LRU list flush有以下几个不同之处:

  1. LRU list flush,由用户线程触发(MySQL 5.6.2之前);而Flush list flush由MySQL数据库InnoDB存储引擎后台srv_master线程处理。(在MySQL 5.6.2之后,都被迁移到page cleaner线程中)
  1. LRU list flush,其目的是为了写出LRU 链表尾部的dirty page,释放足够的free pages,当buf pool满的时候,用户可以立即获得空闲页面,而不需要长时间等待;Flush list flush,其目的是推进Checkpoint LSN,使得InnoDB系统崩溃之后能够快速的恢复。
  1. LRU list flush,其写出的dirty page,需要移动到LRU链表的尾部(MySQL 5.6.2之前版本);或者是直接从LRU链表中删除,移动到free list(MySQL 5.6.2之后版本)。Flush list flush,不需要移动page在LRU链表中的位置。
  1. LRU list flush,由于可能是用户线程发起,已经持有其他的page latch,因此在LRU list flush中,不允许等待持有新的page latch,导致latch死锁;而Flush list flush由后台线程发起,未持有任何其他page latch,因此可以在flush时等待page latch。
  1. LRU list flush,每次flush的dirty pages数量较少,基本固定,只要释放一定的free pages即可;Flush list flush,根据当前系统的更新繁忙程度,动态调整一次flush的dirty pages数量,量更大。

Buffer Pool Dump/Load

在MySQL 5.6.3及之后的版本中,MySQL数据库InnoDB存储引擎提供了将buf pool dump到一个外存文件中的功能。其实对应的操作十分简单,在Buf0dump.cc文件中。

l  Buffer Pool Dump

遍历buf pool的LRU list,对于其中的每一个page,读取page的[space_id, page_no],组成一个64位的数,写到外存文件即可。

l  Buffer Pool Load

读取外存Dump文件,并完成排序(buf_dump_sort)。对于排序后的DUMP数组中的的每一个[space_id, page_no]组合,发起一个异步读IO (buf_read_page_async()),每64个pages,做一次写同步(os_aio_simulated_wake_handler_threads())

总得来说,操作十分简单,但是对应的优势却很明显。通过dump/load可以实现buffer pool的预热,解决了系统重启/切换之后令人头疼的buffer pool预热问题。

Buffer Pool Usage Limitations

此章节记录Buffer Pool的各种使用缺陷,及不同版本的优化策略。

Drop Table

在Mark Callaghan的最新一篇博文:Stalls during DDL中[6](备注:mysqlops网站上也有相关文章分析这个严重问题,链接地址:http://www.mysqlops.com/2012/04/01/mysql-innodb-file-per-table.html),其提到InnoDB存储引擎在进行Drop Table操作时,会短暂hang住整个系统,而且这个hang的时间的长短与Buffer Pool的大小相关。主要原因在于InnoDB在drop table时,会连续两次遍历buf pool LRU 链表,遍历的过程加锁,因此导致系统hang住。那么MySQL数据库InnoDB存储引擎在drop table时为何需要遍历LRU链表呢?

测试准备:

set global innodb_file_per_table=1;

create table todrop (a int, b int, c int) engine = innodb;

insert into todrop select c1,c2,c3 from nkeys;

drop table todrop;

Drop Table Scan LRU List (MySQL 5.5.16)

ha_innobase::delete_table -> fil0fil.c::fil_delete_tablespace()

// 不允许后续的Insert Buffer Merge操作,并等待现有Merge操作完成

space->stop_ibuf_merges = TRUE;

// 不允许Tablespace上后续的I/O与flush操作,并等待现有操作完成

space->is_being_deleted = TRUE;

buf_LRU_invalidate_tablespace();

// 此函数的主要功能,是用于是否drop table在adpative hash中的所有记录

buf_LRU_drop_page_hash_for_tablespace();

// 获取当前buf pool的mutex,遍历buf pool LRU list的准备

buf_pool_mutex_enter();

// 本函数接下来的功能包括:

// 1. 从LRU链表尾部开始遍历

// 2. 对于每一个属于Drop Table的page,判断page中是否有项进入

//      adaptive hash,若有,则收集当前page

// 3. 收集到1024个这样的page后,释放buf pool mutex,集中调用函数

//      buf_LRU_drop_page_hash_batch,释放page在adaptive hash中的项

// 4. 重新获取buf pool mutex,继续遍历buf pool LRU 链表,直至链表头

// 5. 最终,是否buf pool mutex,退出

// 再次遍历buf pool LRU 链表,释放所有drop table对应的page

buf_LRU_invalidate_tablespace_buf_pool_instance();

// 简要的处理流程描述:

// 1. 获取buf pool mutex

// 2. 遍历buffer pool LRU链表

// 3. 若为dirty page,则将dirty page设置为clean page,并从flush list中移除

// 4. 将page从LRU list中移除,并且添加入free list

// 5. 最后,释放buf pool mutex

总结:

l  确实需要遍历两遍buf pool LRU 链表

一遍用于释放adaptive hash中的记录;二遍是用于释放page。

l  第一遍遍历LRU链表,会定期释放buf pool mutex,因此对于系统hang的影响较小;而第二遍会一直持有,对系统hang的影响较大。

建议继续学习

  1. Buffer和cache的区别是什么? (阅读 7,843)
  2. Linux操作系统中内存buffer和cache的区别 (阅读 6,342)
  3. 快速预热Innodb Buffer Pool的方法 (阅读 4,985)
  4. MySQL数据库InnoDB存储引擎 Buffer pool LRU List Flush策略详解 (阅读 4,924)
  5. InnoDB之Dirty Page、Redo log (阅读 4,483)
  6. MySQL数据库InnoDB存储引擎 Insert Buffer实现机制详解 (阅读 4,384)
  7. 小心grep 的buffer (阅读 4,106)
  8. grep 命令的buffer选项 (阅读 3,964)
  9. HBase如何合理设置客户端Write Buffer (阅读 3,724)
  10. 浅谈数据库系统中的cache (阅读 3,104)