揭开Group Replication校验的神秘面纱

在Group Replication 架构中,一个事务在某节点执行,commit之前,会把该事务的所产生的写集(writeset),交给group replication 去校验,看该事务是否与其它节点执行的事务相冲突,如果不冲突,本节点就会commit返回,其余节点就会把该事务的binlog event写入relay log,由applier线程应用。注意,只有多主模式(multi primary)才需要做冲突校验,对于单主模式(single primary)则没有必要,因为不可能冲突!

那么group replication 是如何校验的呢? 事务间的冲突又是如何确定的呢?

这个过程相当神秘,其实理解了又觉得其非常简单,但理解的过程很不简单!

首先我们来看这个writeset 是个什么东西。所谓写集(writeset)就是就是一个事务执行所影响的具体数据行(用主键hash的值来标识),以及执行这个事务的时候,数据库的版本快照!又一个新概念,什么是数据库的版本快照? 其实就是当前数据库已经执行了(commit)哪些事务,说白了就是gtid_executed的值。这样就好理解了。

假如我们有一个事务T1,产生了如下WriteSet

pk_v1 -- UUID:1-9
pk_v2 -- UUID:1-9

这说明事务修改了两条数据, 执行节点的数据库快照是UUID:1-9.那么这样一个WriteSet发送到group replication是如何被校验的呢?

原来在group replicatioin 的校验模块中维护了一个校验集合,使用Certification_info来管理。Certification_info是一个map对象,它里面存的也是数据条目和数据库版本的映射。与WriteSet是一样的。源码中是这样定义的:

typedef std::unordered_map<std::string, Gtid_set_ref *> Certification_info;

一旦某个事务校验通过,它产生的写集就会写入到这个校验集合(Certification_info)中。
假如Certification_info当前存在的校验快照是这样的:
pk_v1 -- UUID:1-8
pk_v2 -- UUID:1-8
...

那么事务T1的校验就会被通过。因为对于第一数据(pk_v1)来说,校验集合中的版本是UUID:1-8 (CV),而事务T1中的版本是UUID:1-9(DV), CV 是DV的子集,对于第二条数据(pk_v2)也是这样。

通过之后校验模块还要判断这个事务T1有没有GTID,如果session 中变量gtid_next=AUTOMATIC,那么它是没有GTID的。这就要为其分配一个,因为它在是在版本UUID:1-9的基础上执行的,所以给他分配下一个数字(GNO)10(当然不会简单的是这样,还有一些结合group_replication_gtid_assignment_block_size的逻辑判断,这里为简单起见,不引入它),那么这个事务的GTID就是UUID:10。

这个事务产生的WriteSet写集也会更新的校验集合(Certification_info)中:
pk_v1 -- UUID:1-9
pk_v2 -- UUID:1-9
...

如果再有事务修改pk_v1和pk_v2只须和上一个校验成功的版本对比即可。如果上一个版本是新来事务的子集那么校验就通过,否则就拒绝。源码实现中也只是一个遍历对比。其注释还是很有帮助的 certifier.cc:630。

{                                                                              
    for (std::list<const char *>::iterator it = write_set->begin();            
         it != write_set->end(); ++it) {                                       
      Gtid_set *certified_write_set_snapshot_version =                         
          get_certified_write_set_snapshot_version(*it);                       
                                                                               
      /*                                                                       
        If the previous certified transaction snapshot version is not          
        a subset of the incoming transaction snapshot version, the current     
        transaction was executed on top of outdated data, so it will be        
        negatively certified. Otherwise, this transaction is marked            
        certified and goes into applier.                                       
      */                                                                       
      if (certified_write_set_snapshot_version != NULL &&                      
          !certified_write_set_snapshot_version->is_subset(snapshot_version))  
        goto end;                                                              
    }                                                                          
  }            

上面的注释逻辑有些绕,大家要多读几遍,反复体会。正过来理解就是:某行数据的校验就是看它在校验集合中的版本(CV)是不是它被修改时所属节点的数据库快照版本(DV)的子集。如果是,就校验通过,否则拒绝。

在源码的校验方法(rpl_gno Certifier::certify)中,真正做校验的就上面那点儿代码。后面的都是校验以后的处理,比如产生GTID, 将新的写集更新到校验结合中等等。感兴趣的同行可以自己去看。

随着事务的增加,这个校验集合会不会越来越大呢? 会的。但不用担心,有单独的算法定时清理。那么什么样的数据可以被清理掉呢?

一旦事务在集群中所有节点都执行完成,那么这个事务的写集就可以从校验集合中清理掉了。它不会再影响任何事务的校验。group replication 各节点会定时将自己已经commit的事务信息发送到集群,然后汇集它们做一个交集,就是在所有节点都commit的事务集合。这个集合可以通过replication_group_member_stats表查到。

mysql> select * from replication_group_member_stats\G
*************************** 1. row ***************************
                              CHANNEL_NAME: group_replication_applier
                                   VIEW_ID: 15295588114993449:10
                                 MEMBER_ID: ec5e51ae-68a0-11e8-8571-52540017b589
               COUNT_TRANSACTIONS_IN_QUEUE: 0
                COUNT_TRANSACTIONS_CHECKED: 2144030
                  COUNT_CONFLICTS_DETECTED: 1
        COUNT_TRANSACTIONS_ROWS_VALIDATING: 0
        TRANSACTIONS_COMMITTED_ALL_MEMBERS: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1-5439749:5446604-5446605
            LAST_CONFLICT_FREE_TRANSACTION: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5446605
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0
         COUNT_TRANSACTIONS_REMOTE_APPLIED: 7
         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 2144027
         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0
*************************** 2. row ***************************
                              CHANNEL_NAME: group_replication_applier
                                   VIEW_ID: 15295588114993449:10
                                 MEMBER_ID: ec725fcd-692c-11e8-bb5f-525400f9f72b
               COUNT_TRANSACTIONS_IN_QUEUE: 0
                COUNT_TRANSACTIONS_CHECKED: 2
                  COUNT_CONFLICTS_DETECTED: 0
        COUNT_TRANSACTIONS_ROWS_VALIDATING: 0
        TRANSACTIONS_COMMITTED_ALL_MEMBERS: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1-5439749:5446604-5446605
            LAST_CONFLICT_FREE_TRANSACTION: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5446605
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE: 0
         COUNT_TRANSACTIONS_REMOTE_APPLIED: 1
         COUNT_TRANSACTIONS_LOCAL_PROPOSED: 1
         COUNT_TRANSACTIONS_LOCAL_ROLLBACK: 0

结果集中有一个字段TRANSACTIONS_COMMITTED_ALL_MEMBERS,就是当前各节点均已执行的事务集合(stable set)。由这个结合再与校验集合Certification_info中的个条目做交集,如果校验结合中某条目是TRANSACTIONS_COMMITTED_ALL_MEMBERS的子集。那么此条目就可以被清理。源码中实现的方法:void Certifier::garbage_collect()

{                                                                           
DBUG_ENTER("Certifier::garbage_collect");                                   
DBUG_EXECUTE_IF("group_replication_do_not_clear_certification_database",    
                { DBUG_VOID_RETURN; };);                                    
                                                                            
mysql_mutex_lock(&LOCK_certification_info);                                 
                                                                            
/*                                                                          
  When a transaction "t" is applied to all group members and for all        
  ongoing, i.e., not yet committed or aborted transactions,                 
  "t" was already committed when they executed (thus "t"                    
  precedes them), then "t" is stable and can be removed from                
  the certification info.                                                   
*/                                                                          
Certification_info::iterator it = certification_info.begin();               
stable_gtid_set_lock->wrlock();                                             
while (it != certification_info.end()) {                                    
  if (it->second->is_subset(stable_gtid_set)) {                             
    if (it->second->unlink() == 0) delete it->second;                       
    certification_info.erase(it++);                                         
  } else                                                                    
    ++it;                                                                   
}                                                                           
stable_gtid_set_lock->unlock();    

更多信息还请阅读源码。这部分实现逻辑确实比较绕。描述起来也很困难。通过查阅文档,再结合源码实现,特别是源码的注释,反复体会方能理解。

参考资料:

WL#6833
阅读原文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容