怎么爬before after之间的内容_MySQL的after_sync与after_commit性能之争源码剖析与情景测试...

3c02bac8bdc68dd7d8e9463bc69c2dcc.png

背景

在已知环境和认知下,更新热点数据时,after_sync性能会很明显不如after_commit。同事认为,after_sync模式下,等待从库ACK时,会持有更新记录的行级锁;与此相反,after_commit模式下,因为等待ACK时,事务已经提交,不再持有事务内锁;这意味着,每个事务持有锁的时间延长了一段ACK时间,写入耗时一定会随着事务之间的锁竞争频繁度与ACK耗时体现出来。然而,令人意外的是,在另一位同事,做压测时,测试结果又打脸了,在无热点数据时,after_sync性能依然不如after_commit。因此,又一次引来了笔者的探索与求证欲望。

源码分析

根据笔者之前的文章《金融级MySQL切换卡主源码剖析》,找到LOCK_commit互斥锁,以此为入口,在源码中搜索“LOCK_commit”,迅速找到sql/http://binlog.cc的9570行,关键代码如下,

很显然,after_sync一定是在sync队列之后,commit队列之前。而进入commit队列的必要条件是一定要获取LOCK_commit互斥锁,故after_sync参数的处理一定就在附近。
9568

依次往下看,很快,就可以在9581行看到call_after_sync_hook函数的调用。顾名思义,很容易让人联想到after_sync,鉴于文章《金融级MySQL切换卡主源码剖析》的分析经验。找到了call_after_sync_hook函数,就开始跟随笔者一步步分析下去吧。在函数中找到RUN_HOOK调用,一步步拆解如下,

// 调用方式

找到观察器调用宏FOREACH_OBSERVER定义,

观察者模式请自行了解,因为笔者非正统开发人士,理解有限
#define FOREACH_OBSERVER(r, f, thd, args)                               

到此,可以看到,这是在遍历所有的观察者。从observer_info_iter找出观察者基类Delegate。接着,找到具有after_sync函数实现的子类Binlog_storage_delegate,其observer为Binlog_storage_observer。而Binlog_storage_observer定义如下,

// 接口定义

而repl_semi_report_binlog_sync函数内容分析如下,

if 

以上代码,我们可以看到,commitTrx函数(即ReplSemiSyncMaster::commitTrx函数实现了半同步的ACK等待)。很显然,我们可以大胆猜测,在after_commit模式也会调用这个函数。在代码中搜索commitTrx,很快可以找到函数repl_semi_report_commit,如下,

97 

从rpl_semi_sync_master_wait_point就可以很清楚的确认,after_commit模式,也是调用此函数。而现在,我们需要确认的是,after_commit模式,在什么时候去调用ACK等待。

根据以上的查找逻辑,依葫芦画瓢,反查:从trans_observer中找到rpl_semi_sync_master_wait_point函数的接口定义。接着,去找它的观察者子类实现接口,找到Trans_delegate子类,实现函数接口为Trans_delegate::after_commit。而该函数的调用,根据上面的分析经验,我们可以很容易地拼接调用的方式,如下:

RUN_HOOK(tran, after_commit, (queue_head, log_file, pos))

以此,搜索代码,找到调用处。鉴于笔者搜索经验,首先搜索“RUN_HOOK(tran”很快就找了真正调用方式,如下:

(

有以下3次调用:

  • MYSQL_BIN_LOG::process_after_commit_stage_queue函数
用于开启opt_binlog_order_commits时,刚好处理完commit队列,释放LOCK_commit互斥锁后。
  • MYSQL_BIN_LOG::finish_commit函数
用于处理flush队列、sync队列以及commit队列处理异常的情况,暂不考虑
  • ha_commit_low函数
事务提交接口,包括Binlog提交与InnoDB存储引擎层提交。针对组提交的正常情况,此处的after_commit逻辑也不会被调用。

流程梳理

根据以上的源码分析,流程梳理如下,

两阶段提交相关代码的分析由于过于复杂,有时间再用其它文章说明
  • prepare阶段

a7dbdb91077408b3556d57bd25d4e33c.png
  • commit阶段

da7e3990e65e10560821e129509cab03.png

很显然,区别主要两点点:

  1. after_sync模式下,commit队列会持有LOCK_commit互斥锁,下一队列一定会挂住
  2. after_sync模式下,commit队列中的线程会持有事务内的锁,凡是涉及这些锁竞争的其它队列中的事务一定会挂住

情景测试

MySQL版本:5.7.24
参数:sync_binlog = 1
参数:innodb_flush_log_at_trx_commit = 1
测试方式:40个线程并发,0.5s写入一次
分析方法:每种测试场景下,通过perf采样两分钟的数据,通过火焰图对比分析
命令参考:perf record -F 99 -p process_id -g -- sleep 30; perf script | ./stackcollapse-perf.pl > out.perf-folded; ./flamegraph.pl out.perf-folded > perf-kernel.svg
  • 无热点数据
after_sync模式

0f0a3d890ea2d41c48b756e007f52b41.png
after_commit模式

abd0ebbb000d73730bf69491965df4ce.png

分析:

after_sync -> after_commit
Binlog_storage_delegate::after_flush: 0.17% -> 0.13% // 获取互斥锁LOCK_binlog_更频繁,即更频繁地锁住Binlog
MYSQL_BIN_LOG::change_stage: 0.88% -> 2.10% // 获取锁的时间延长
MYSQL_BIN_LOG::flush_cache_to_file: 0.44% -> 0.45% // 写binlog文件缓存请求稍微有点增长
MYSQL_BIN_LOG::process_commit_stage_queue: 0.44% -> 0.77% // 存储引擎层提交次数增多
MYSQL_BIN_LOG::process_flush_stage_queue: 1.61% -> 1.87% // Binlog线程缓存写文件缓存增多,重做日志刷盘增多
MYSQL_BIN_LOG::sync_binlog_file: 0.66% -> 0.72% // Binlog刷盘请求增多
MYSQL_BIN_LOG::process_after_commit_stage_queue: _.__% -> 1.52 // after_sync模式下,几乎没有after_commit相关逻辑的耗时
call_after_sync_hook: 0.10% -> 0.06% // after_commit模式下,after_sync相关逻辑的耗时没有完全消失

结论:after_sync模式下,由于组提交而更便于集中处理,从半同步插件的调用次数、组提交队列的流转次数、Binlog的请求写次数与刷盘次数,重做日志的刷盘次数等,都出现了减少。因此,after_sync性能是优于after_commit的。

  • 有热点数据
after_sync模式

218ddae837298efc27428911c569168c.png
after_commit模式

5d92ec69499573a0046861ce6a4d3d7b.png

分析:

MYSQL_BIN_LOG::ordered_commit: 2357 samples, 15.19% -> 3954 samples, 9.75%
// 提交请求次数更多
lock_trx_release_locks: 1634 samples, 10.53% -> 502 samples, 1.24%
// 从lock_rec_grant函数可以看到,获取记录锁的时间大大增加了

看图已经一目了然,在after_sync模式下,lock_trx_release_locks函数占比非常突出。大致估算下,从after_sync模式到after_commit模式,CPU时间由原来的10.53%/15.19%=69.3%降到了1.24%/9.75%=12.7%

结论:有热点数据,不用废话,after_commit模式,选它,选它,就选它!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值