MySQL 5.7: Page Cleaner的刷脏问题

之前我已经写过一篇博客,讨论过在flush LRU_LIST/FLUSH_LIST时,5.7对其做的优化,总的来说,就是使用类似Hazard Pointer的方式,避免在flush的过程中重复扫描LIST,将时间复杂度从O(N*N)下降到了O(N)。有兴趣的同学可以翻阅下这篇博客:http://mysqllover.com/?p=1031

本文的目的主要是补充下5.7目前所做的多个page cleaner的实现思路,社区相关的bug讨论,以及我近期对page cleaner所做的一些优化工作。

支持多个Page cleaner

为了提升扩展性和刷脏效率,在5.7.4版本里引入了多个page cleaner线程。从而达到并行刷脏的效果。

在该版本中,Page cleaner并未和buffer pool绑定,其模型为一个协调线程 + 多个工作线程,协调线程本身也是工作线程。因此如果innodb_page_cleaners设置为8,那么就是一个协调线程,加7个工作线程

协调线程的入口函数为buf_flush_page_cleaner_coordinator

工作线程的入口函数为buf_flush_page_cleaner_worker

在启动时会初始化一个slot数组,大小为buffer pool instance的个数(buf_flush_page_cleaner_init)。

协调线程在决定了需要flush的page数和lsn_limit后,会设置slot数组,将其中每个slot的状态设置为PAGE_CLEANER_STATE_REQUESTED, 并设置目标page数及lsn_limit,然后唤醒worker线程 (pc_request)

worker线程被唤醒后,从slot数组中取一个未被占用的slot,修改其状态,表示已被调度,然后对该slot所对应的buffer pool instance进行操作。

为了支持对单个bp instance进行LRU/FLUSH_LIST的刷新,对原有代码做了大量的改动,worker线程可以直接调用buf_flush_LRU_list 及buf_flush_do_batch 指定buffer pool进行flush操作。 互相之间不干扰,因此可以并行刷脏。 改动整体而言比较简单。

由于当前版本page cleaner是不和buffer pool绑定的,因此,最好不要将page cleaner的个数设定的大于buffer pool instance的个数。

如果当前实例不活跃,即没有负载时,则单独由协调线程做100%的刷脏。

worklog:

http://dev.mysql.com/worklog/task/?id=6642

代码比较简单,不展开阐述,感兴趣的可以读读补丁:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/7189

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/7244

进一步改进Multi Page Cleaner

在MySQL 5.7.5里进一步改进了multi page cleaner特性,让其支持在crash recovery和shutdown时能够应用多个page cleaner特性,来加快崩溃恢复和关闭实例的速度。

实现方式也比较简单,例如对于recovery过程中,如果需要刷脏时,不主动刷脏,而是唤醒page cleaner,然后等待page cleaner完成。

PATCH:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8489

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8522

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8532

社区相关BUG

http://bugs.mysql.com/bug.php?id=68481

期望允许page cleaner做furious flush,而不是每秒做一次,对于写入负载很猛的场景,很容易到达同步checkpoint点

http://bugs.mysql.com/bug.php?id=70500

不管是否active,总是做LRU FLUSH。 因为在某些场景下,可能没什么用户负载,checkpoint age很低,但诸如purge操作需要很多free page时, 这时候我们其实期望page cleaner 负责清出更多的空闲block。

事实上, webscalesql已经这么做了。感兴趣的可以直接去checkout webscalesql的代码看看

http://bugs.mysql.com/bug.php?id=71411

buf_do_LRU_batch函数的返回值表示从LRU上flush的脏页数,但却把压缩表上驱逐的解压页也纳入了统计。这可能会引起某些innodb_metrics的错误值。 这还会影响到page cleaner的刷脏策略。

http://bugs.mysql.com/bug.php?id=74637

让page cleaner的刷新策略更加自适应。应该根据redo space 和free list做出自适应的page cleaner策略调整,我(id zhai weixiang) 在bug中也回复了可行的策略:对于lru_list,percona server有更加聪明的做法,独立出LRU线程并根据free list长度调整sleep时间;对于flush_list,我们可以对sleep的时间做动态调整,根据max_modified_age_sync来进行估算。

可行的改进

针对page cleaner的问题,我基于阿里内部的版本中做了些修改 (感兴趣的阿里同学可以找我拿测试branch),大概包含如下几点:

  1. 分拆LRU FLUSH到独立线程,根据FREE LIST长度自适应调整刷LRU策略 (from percona)
  2. PAGE CLEANER支持multi page cleaner (from 5.7)。并根据redo space 计算自适应的刷脏频率
  3. 从用户线程移除SINGLE PAGE FLUSH,总是由LRU FLUSH线程来释放空闲page.
  4. MULTI PAGE CLEANER依然会引起double write buffer的单点竞争。为了解决这个问题,引入多个double write file,每个double write buffer文件对应特定的buffer pool instance.

原创文章,转载请注明: 转载自Simple Life

本文链接地址: MySQL 5.7: Page Cleaner的刷脏问题

Post Footer automatically generated by wp-posturl plugin for wordpress.


Comments

Leave a Reply

Your email address will not be published. Name and email are required


Current month ye@r day *