从源码探究MySQL5.7高吞吐事务量的背后操手
《从源码探究MySQL5.7高吞吐事务量的背后操手》要点: 大家都知道在MySQL中,在事务真正COMMIT之前,会将事务的binlog日志写入到binlog文件中.在MySQL的5.7版本中,提供了所谓的无损复制功能,该功能的作用就是在主库的事务对其他的会话线程可见之前,就将该事务的日志同步到从库,保证了事务可以安全地无丢失地复制到从库. 下面我们从源码来分析MySQL的事务提交以及事务在何时将binlog复制到从库的. MYSQL_BIN_LOG::ordered_commit,这个是事务在binlog阶段提交的核心函数,通过该函数,实现了事务日志写入binlog文件,以及触发dump线程将binlog发送到Slave,在最后的步骤,将事务设置为提交状态. 我们来分析MYSQL_BIN_LOG::ordered_commit这个函数的核心过程,该函数位于binlog.cc文件中. 源码分析MYSQL_BIN_LOG::ordered_commit,这个函数,核心步骤如下: 第一步骤:flush Stage#1: flushing transactions to binary log: 步骤1 :将事务的日志写入binlog文件的buffer中,函数如下:
从5.6开始,MySQL引入了Group Commit的概念,这样可以避免每个事务提交都会锁定一次binlog. 另外,还有一个用处,就是MySQL5.7的基于logical_clock的并行复制.在一个组里面(其实是一个队列),这一组队列的头事务是相同的,因此这一组事务的last_committed(上一组的最后一个提交的事务)的事务也是同一个.我们都知道,last_committed相同的事务,是可以在从库并行relay(重演)的. 该函数process_flush_stage_queue的作用,就是将commit队列中的线程一个一个地取出,然后执行子函数 flush_thread_caches(head);循环的代码如下:将各自线程中的binlog cache写入到binlog中.
第二步骤:SYNC to disk Stage#2: Syncing binary log file to disk 第二步:将binlog file中cache的部分写入disk.但这个步骤参数sync_binlog起决定性的作用. 我们来看看源码,除了这些还有哪些细节步骤,听完源码分析之后,你应该有新的收获与理解.在执行真正的将binlog写到磁盘之前,会进行一个等待,函数如下:
等待的时间由MySQL参数文件中的binlog_group_commit_sync_delay,binlog_group_commit_sync_no_delay_count 这两参数共同决定.第一个表示该事务组提交之前总共等待累积到多少个事务,第二个参数则表示该事务组总共等待多长时间后进行提交,任何一个条件满足则进行后续操作. 因为有这个等待,可以让更多事务的binlog通过一次写binlog文件磁盘来完成提交,从而获得更高的吞吐量. 接下来,就是执行sync_binlog_file,该函数会用到MySQL参数文件中sync_binlog参数的值,如果为0,则不进行写磁盘操作,由操作系统决定什么时候刷盘,如果为1,则强制进行写磁盘操作. 再接下来,执行update_binlog_end_pos函数,用来更新binlog文件的最后的位置binlog_end_pos,该binlog_end_pos是一个全局的变量.在执行更新该位置之前,先得找到最后一个提交事务的线程(因为是Group Commit,多个事务排队提交的机制).因为已经将要提交事务的线程组成了一个链表,所以通过从头到尾找,可以找到最后一个线程.代码如下:
接下来,我们来看一下这个函数update_binlog_end_pos.这个函数很简单,传入一个pos,然后将其赋值给全局变量binlog_end_pos,接下来就是最核心的一行代码,signal_update(),发送binlog更新的信号.因此从主库同步binlog到从库的dump线程,会接收到这个binlog已有更新的信号,然后启动dump binlog的流程. 函数update_binlog_end_pos的完整代码如下:
Semi-sync通过上面的步骤介绍,我们可以看到在binlog文件的最新位置更新的时候,就已经通过signal_update函数发送信号给binlog的dump线程,该线程就可以将事务的binlog同步到从库,从库接收到日志之后,就可以relay日志,实现了主从同步. 因此,再次重复说明一下,按照上面的解释,在事务真正提交完成之前就开始发送了binlog已经更新的信号,dump线程收到信号,即可以进行binlog的同步.那Semisync的作用是什么呢? 实际上,有没有Semisync机制,对上面介绍的MySQL的有关事务提交中关于binlog的流程都是一样的.Semisync的作用,只是主从之间的一个确认过程,主库等待从库返回相关位置的binlog已经同步到从库的确认(而实际实现则是等待dump线程给用户会话线程一个回复),没有得到确认之前(或者等待时间达到timeout),事务提交则在该函数(步骤)上等待直至获得返回. 具体执行binlog已经同步到某个位置的的确认函数为repl_semi_report_binlog_sync,函数如下:
通过观察上述函数,我们可以看到有个rpl_semi_sync_master_wait_point变量与WAIT_AFTER_SYNC比较,如果不相等,则直接返回,直接返回则表示不需要在此时此刻确认binlog是否已经同步.而这个变量的取值来自于半同步参数semi_sync_master_wait_point的初始设置,我们可以设置为after_sync与after_commit. 这两个参数含义的区别是:after_sync是在将binlog sync到disk之后(具体是否真正sync由参数sync_binlog的值决定)进行日志同步确认,而after_commit是将事务完成在InnoDB里面提交之后再进行binlog的同步确认.两者确认的时间点不同,after_sync要早于after_commit. 接下来,我们来看repl_semisync.commitTrx 这个函数,这个函数有两个传入参数,一个是binlog文件,一个binlog文件的位移.我们来看这个函数的含义吧.算了,还是直接用源码的注释来解释吧. 上面的注释说得相当清楚,就是该commiTRX函数会等待binlog-dump返回已经同步到该位置的报告,如果还没有同步到该位置,则继续等待,直到超时返回. 当会话线程收到该函数的返回时,事务的提交过程继续往下走,直至在InnoDB真正提交. 总结通过上述对MySQL的事务提交过程中的前段分析,应该可以了解Semi-sync的同步机制与异步机制的区别. Semi-sync的主从同步机制与异步机制在同步的处理方式上无任何区别,唯一的区别就是Semi-sync在事务提交中段(假如设置为after_sync)或者提交后的阶段(after_commit),有一个验证该事务涉及的binlog是否已经同步到从库.而这个同步验证,会拉长整个事务的提交时间,因为事务提交在数据库中几乎是串行(如果按Group Commit为一个单位,就算是完全地串行),这是影响MySQL吞吐量的关键点,当这个关键点被拉长,对全局的影响就被放大.虽然仅仅多了这么一个确认的动作,但主库处于Semi-sync的同步状态与异步状态的吞吐量相比,相差了好几倍. 上述解释就是其真正的原因. 作者介绍: 徐春阳,笔名happypig.曾任职于阿里、百度、京东、即刻搜索,目前供职民生银行总行科技部,从事数据库运维工作.对开源数据库MySQL有较深入的研究,个人博客:xuchunyang.com. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- vim – 在使用NERD树时使用VCSCommand插件提交目录
- Docker入门之快速安装和卸载使用Centos7
- 如何在TypeScript中的ReactiveX / rxjs 5中使用exhaustMap
- Vim:错误格式匹配所有内容
- angular – Ion-list应该自动在列表项上添加箭头吗?
- webservice(三) 使用JDK的EndPoint和cxf框架分别发布webser
- angularjs – Angular UI Router – 如何在切换视图时保留视
- scala – 对Java枚举使用类型别名
- scala – Play框架:将XML解析为模型
- AngularJS 最常用的八种功能