天天看点

[MySQL 5.6] page cleaner线程的效率问题

最近在测试5.6.11的写性能,当我完成一项测试,关闭workload,习惯性设置innodb_max_dirty_pages_pct为0,然后等待脏页刷完再shutdown。

发现存在刷脏的抖动:

time      flushed    innodb_data_written

11:49:57  3692        117.3m

11:49:58  1125        33.5m|

11:49:59  4187        134.6m

11:50:00  1241         35.0m

11:50:01  4076        127.4m

从pstack的采样结果来看,page cleaner线程频繁出现这样的backtrace。

buf_flush_page_cleaner_thread->page_cleaner_sleep_if_needed->os_thread_sleep

加了两个计数来监控,也发现page cleaner线程平均sleep时间过长(750,000 ms 左右)。

那么为什么会出现波动呢?

检查发现,机器上有一个heartbeat脚本每隔两秒钟更新一条记录。这会导致如下条件成立:

2385                 if (srv_check_activity(last_activity)

2386                     || buf_get_n_pending_read_ios()

2387                     || n_flushed == 0) {

2388                         page_cleaner_sleep_if_needed(next_loop_time);

2389                 }

srv_check_activity(last_activity) 为非0值。目前的判断太过粗糙了。一条简单的update 会造成page cleaner线程的巨大波动。

好吧,对我而言办法比较土,临时的解决方案就是增加一个变量,来限制最大sleep时间,这样就可以获得一个平缓的刷脏频率:

11:51:43   3971         120.6m

11:51:44   3987         124.6m

11:51:45   3859         124.6m

11:51:46   3992         121.0m

11:51:47   3855         120.7m

另外,当srv_check_activity(last_activity)返回非0值后,会走不同的逻辑:

2393                 if (srv_check_activity(last_activity)) { 2394                         last_activity = srv_get_activity_count(); 2395 2396                         /* flush pages from end of lru if required */ 2397                         n_flushed = buf_flush_lru_tail(); 2398 2399                         /* flush pages from flush_list if required */ 2400                         n_flushed += page_cleaner_flush_pages_if_needed();

buf_flush_lru_tail() : 依次遍历每个buffer pool instance,从lru尾部开始扫描,直到第srv_lru_scan_depth个page停止,按批次刷lru,每次期望刷100个page(一个chunk), 每个bp会轮srv_lru_scan_depth/100次循环

每一个chunk的循环,都是从lru的尾部开始的,因为这中间会去释放bp的mutex。

这样问题就比较明显了,如果有很多脏页,例如,我们假设lru上的都是脏页.从函数buf_flush_lru_list_batch的逻辑我们可以知道

1.如果这个page是脏的,不可以替换,将其io-fix,并分发io请求

2.回到lru尾部,跳过io-fix的page,发发现新的脏页,同样将其io-fix,并返回到lru尾部。

可以看到这里时间复杂度是o(n*n).当然如果是快速存储设备,可能在回到lru尾部重新扫描时,之前iofix的page已经完成了io,因此可以直接放到freelist上。因此快速存储设备最优可以到达o(n)

按照inaam的说法,5.6.12对此会有优化,拭目以待。另外在5.6.12中,可能会有很多sleep被替换成condition wait,希望这些能对写负载有帮助。

继续阅读