阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

从源码解读Mysql 5.7性能和数据安全性的提升

83次阅读
没有评论

共计 4052 个字符,预计需要花费 11 分钟才能阅读完成。

导读 众所周知,在 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 中,函数如下:

process_flush_stage_queue(&total_bytes,&do_rotate, &wait_queue);

从 5.6 开始,mysql 引入了 group commit 的概念,这样可以避免每个事务提交都会锁定一次 binlog. 另外,还有一个用处,就是 mysql 的 5.7 的基于 logical_clock 的并行复制。在一个组里面(其实是一个队列),这一组队列的头事务是相同的,因此这一组事务的 last_committed(上一组的最后一个提交的事务)的事务也是同一个。我们都知道,last_committed 相同的事务,是可以在从库并行 relay(重演)的。该函数 process_flush_stage_queue 的作用,就是将 commit 队列中的线程一个一个的取出,然后执行子函数 flush_thread_caches(head); 循环的代码如下:将各自线程中的 binlog cache 写入到 binlog 中。

/* Flush thread caches to binary log. */

for (THD *head= first_seen ; head ; head = head->next_to_commit)

{std::pair<int,my_off_t>result= flush_thread_caches(head);

total_bytes+= result.second;

if(flush_error == 1)

flush_error= result.first;

#ifndef DBUG_OFF

no_flushes++;

#endif

}
第二步骤 SYNC to disk

Stage#2: Syncing binary log file to disk

第二步:将 binlog file 中 cache 的部分写入 disk. 但这个步骤参数 sync_binlog 起决定性的作用。

我们来看看源码,除了这些还有哪些细节步骤,看完源码分析之后,你应该有新的收获与理解。

在执行真正的将 binlog 写到磁盘之前,会进行一个等待,函数如下:

stage_manager.wait_count_or_timeout(opt_binlog_group_commit_sync_no_delay_count,

opt_binlog_group_commit_sync_delay,

Stage_manager::SYNC_STAGE);

等待的时间由 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, 多个事务排队提交的机制)。因为已经将要提交事务的线程组成了一个链表,通过从头到尾找,可以找到最后一个线程。代码如下:

if(update_binlog_end_pos_after_sync)

{

THD*tmp_thd= final_queue;

while(tmp_thd->next_to_commit != NULL)

tmp_thd= tmp_thd->next_to_commit;

update_binlog_end_pos(tmp_thd->get_trans_pos());

}

接下来,我们来看一下这个函数 update_binlog_end_pos,这个函数很简单,传入一个 pos, 然后将其赋值给全局变量 binlog_end_pos,接下来就是最核心的一行代码,signal_update(),发送 binlog 更新的信号,因此从主库同步 binlog 到从库的 dump 线程,会接收到这个 binlog 已有更新的信号,然后启动 dump binlog 的流程。

函数 update_binlog_end_pos 的完整代码如下。

semisync

通过上面的步骤介绍,我们看到,在 binlog 文件的最新位置更新的时候,就已经通过 signal_update 函数发送信号给 binlog 的 dump 线程,该线程就可以将事务的 binlog 同步到从库,从库接收到日志之后,就可以 relay 日志,实现了主从同步。因此,再次重复说明一下,按照上面的解释,在事务真正提交完成之前就开始发送了 binlog 已经更新的信号,dump 线程收到信号,即可以进行 binlog 的同步。而 semisync 的作用是什么呢?

实际上,有没有 semisync 机制,上面介绍的 mysql 的有关事务提交中关于 binlog 的流程都是一样的,semisync 的作用,只是主从之间的一个确认过程,主库等待从库返回相关位置的 binlog 已经同步到从库的确认,(而实际实现则是等待 dump 线程给用户会话线程一个回复),没有得到确认之前(或者等待时间达到 timeout),事务提交则在该函数(步骤)上等待直至获得返回。具体执行 binlog 已经同步到某个位置的的确认函数为 repl_semi_report_binlog_sync,函数如下:

int repl_semi_report_binlog_sync(Binlog_storage_param *param,

constchar *log_file,

my_off_t log_pos)

{if(rpl_semi_sync_master_wait_point == WAIT_AFTER_SYNC)

return repl_semisync.commitTrx(log_file, log_pos);

return 0;

}

通过观察上述函数,我们可以看到有个 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 文件的位移。我们来看这个函数的含义吧。算了,还是直接用源码的注释来解释吧。

从源码解读 Mysql 5.7 性能和数据安全性的提升

上面的注释说得相当清楚,就是该 commiTRX 函数会等待 binlog-dump 返回已经同步到该位置的报告,如果还没有同步到该位置,则继续等待,直到超时返回。

当会话线程收到该函数的返回时,事务的提交过程继续往下走,直至在 innodb 真正提交。

总结

通过上述对 mysql 的事务提交过程中的前段分析,应该可以了解 semi-sync 的同步机制与异步机制的区别,semi-sync 的主从同步机制与异步机制在同步的处理方式上无任何区别,唯一的区别就是 semi-sync 在事务提交中段(假如设置为 after_sync)或者提交后的阶段(after_commit), 有一个验证该事务涉及的 binlog 是否已经同步到从库,而这个同步验证,会拉长整个事务的提交时间,因为事务提交在数据库中几乎是串行(如果按 group commit 为一个单位,就算是完全地串行),是影响 mysql 吞吐量的关键点,当这个关键点被拉长,所以对全局的影响就被放大。虽然仅仅多了这么一个确认的动作,但当主库处于 semisync 的同步状态与异步状态的吞吐量相比,相差了好几倍。上述解释就是其真正的原因。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计4052字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中