注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

网易杭研后台技术中心的博客

 
 
 
 
 

日志

 
 

基于Row的MySQL并行复制实现分析(二)  

来自raolh   2013-06-07 18:54:45|  分类: MySQL |举报 |字号 订阅

  下载LOFTER 我的照片书  |
上一篇分析了基于Row格式binlog的并行复制的大体实现,并指明了该并行复制方案不是crash safe的。会存在这样一种情况:多个事务由分析线程分发给不同的执行线程去执行,各事务在从数据库上的执行先后顺序不确定,可能主数据库上后发生的事务已经执行完成,而主上先发生的事务还未开始执行。此时,relay log info中记录的执行位置还在未执行事务的前面,如果此时发生系统崩溃,重启之后系统会从未执行的事务开始执行,则主上后发生且已经执行过的事务会再次被执行一遍。因此会造成主从数据库的不一致。

这种数据不一致产生的原因是:事务被分发给各个执行线程后它们的执行顺序是不确定的,为了解决这个问题MariaDB的 Nielsen实现了一种in-order commit的进制,使得在多个执行线程中的事务按照它们在master上提交的顺序提交。以防止master上后提交的事务在slave上先提交,从而导致crash重启后数据的不一致。in-order commit实现的原理是:分析线程从relay log读取出事务event的顺序与它们在master上发生顺序相同,所以在分析线程中可以确定事务将要提交的顺序。

为了确定事务发生的先后顺序,给事务结构transaction_st添加了若干个成员:

class transaction_st {
。。。
wait_for_commit commit_orderer; // 用于等待其他事务和唤醒等待事务的信息结构。
uint64 wait_commit_id; // 需要等待先提交的事务的id号
transaction_st *wait_commit_trans; // 需要等待先提交的事务的信息结构
uint64 own_commit_id; // 本事务的id号
}

事务的id号是由分析线程确定的,它是一个递增的整型变量。一个事务需要等待被分析线程处理的前一个事务提交完成,才能进行提交。并且一个事务只能等待一个事务提交,不能等待多个。为了记录事务的提交情况,在relay log info结构中添加了如下成员:

class Relay_log_info {
。。。
uint64 last_trans_id; // 最后分发的事务的id
transaction_st *last_trans; // 最后分发的事务的信息结构
uint64 last_committed_id; // 最后一次提交的事务的id
}

wait_for_commit中记录了若干等待过程需要使用的信息,结构如下:

struct wait_for_commit{
。。。
wait_for_commit *subsequent_commits_list; // 等待该事务提交的事务链表
wait_for_commit *next_subsequent_commit; // 等待相同事务的下一个事务指针
wait_for_commit *waitee; // 等待提交的事务
void *opaque_pointer; // 用于group commit,记录事务结构信息指针
bool waiting_for_commit; // 是否需要等待其他事务
bool wakeup_subsequent_commits_running; // 是否正在唤醒其他等待的事务
}

事务在提交之前,通过判断本事务的wait_commit_id和rli的last_trans_id来决定是否需要进行等待,既如果等待的事务还没有进行提交,则需要进行等待。如果需要等待,则首先要填写事务的commit_orderer信息,具体过程是:将本事务的
waitee指向等待事务的commit_orderer结构;将本事务的commit_orderer加入到等待事务的subsequent_commits_list链表当中。具体的执行过程如下:

Transfer_worker::execute_transcation(transaction_st * trans){  
。。。
  if (trans->wait_commit_id > rli->last_committed_id){ register_wait_for_prior_commit (); //将本事务加入到等待事务的链表 }
       execute_single_transaction();                     // 进入执行事务的函数
       unregister_wait_for_prior_commit();                     // 将本事务从等待事务的链表中删除
if (rli->last_committed_id < trans->own_commit_id)     // 更新rli中最后提交的事务的id好
rli->last_committed_id= trans->own_commit_id;
       wakeup_subsequent_commits ();                           // 唤醒等待本事务的事务
 。。。
}

为了实现在group commit的情况下多个执行线程提交的事务有序,in-order commit做了如下修改:group commit提交的事务首先加入到一个事务链表,之前的实现方式是事务线程自己将事务信息结构加入到事务链表中,这样事务加入到链表中的顺序就是不确定的。修改之后的实现方式是事务线程的事务信息结构由它等待的事务线程加入到事务链表中。例如:T2等待T1。若T2线程执行到提交阶段发现T1还没有提交,则将T2的事务信息记录到成员commit_ordereropaque_pointer指针中,并进入条件等待。当事务T1执行到提交阶段时,由于它没有需要等待的事务,所以直接将自己的事务信息加入到事务链表中,发现T2在等待它,则将T2的事务信息也加入到事务链表中。当链表中的事务被组提交之后,只需要直接换新T2即可,T2不需要再做其他的事情。
  评论这张
 
阅读(849)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017