本文是由爱可生运维团队出品的「MySQL专栏」系列文章,内容来自于运维团队一线实战经验,涵盖MySQL各类特性的实践,优化案例,数据库架构,HA,监控等,有扫雷功效。php
爱可生开源社区持续运营维护的小目标:mysql
- 每周至少推送一篇高质量技术文章
- 每个月研发团队发布开源组件新版
- 每一年1024开源一款企业级组件
- 2019年至少25场社区活动
欢迎你们持续关注~git
MGR做为MySQL原生的高可用方案,它的基于共识协议的同步和决策机制,看起来也更为先进。吸引了一票用户积极尝试,但愿经过MGR架构解决RPO=0的高可用切换。在实际使用中常常会遇到由于网络抖动的问题形成集群故障,最近咱们某客户就遇到了这类问题,致使数据不一致。github
问题现象算法
这是在生产环境中一组MGR集群,单主架构,咱们能够看到在相同的GTID86afb16f-1b8c-11e8-812f-0050568912a4:57305280下,本应执行相同的事务,但binlog日志显示不一样事务信息。sql
SET @@SESSION.GTID_NEXT= '86afb16f-1b8c-11e8-812f-0050568912a4:57305280'/*!*/;# at 637087357#190125 15:02:55 server id 3136842491 end_log_pos 637087441 Querythread_id=19132957 exec_time=0 error_code=0SET TIMESTAMP=1548399775/*!*/;BEGIN/*!*/;# at 637087441#190125 15:02:55 server id 3136842491 end_log_pos 637087514 Table_map:`world`.`IC_WB_RELEASE` mapped to number 398# at 637087514#190125 15:02:55 server id 3136842491 end_log_pos 637087597 Write_rows: table id 398flags: STMT_END_FBINLOG 'n7RKXBP7avi6SQAAABov+SUAAI4BAAAAAAEAB2ljZW50ZXIAFUlDX1FVRVJZX1VTRVJDQVJEX0xP'/*!*/;### INSERT INTO `world`.`IC_WB_RELEASE`### SET
SET @@SESSION.GTID_NEXT= '86afb16f-1b8c-11e8-812f-0050568912a4:57305280'/*!*/;# at 543772830#190125 15:02:52 server id 3136842491 end_log_pos 543772894 Querythread_id=19162514 exec_time=318 error_code=0SET TIMESTAMP=1548399772/*!*/;BEGIN/*!*/;# at 543772894#190125 15:02:52 server id 3136842491 end_log_pos 543772979 Table_map:`world`.`IC_QUERY_USERCARD_LOG` mapped to number 113# at 543772979#190125 15:02:52 server id 3136842491 end_log_pos 543773612 Delete_rows: table id113 flags: STMT_END_FBINLOG 'nLRKXBP7avi6VQAAADNRaSAAAHEAAAAAAAEAB2ljZW50ZXIADUlDX1dCX1JFTEVBU0UACw8PEg8'/*!*/;### DELETE FROM `world`.`IC_QUERY_USERCARD_LOG`### WHERE
从以上信息能够推测,primary节点在这个GTID下对world.IC_WB_RELEASE表执行了insert操做事件没有同步到secondary节点,secondary节点收到主节点的其余事件,形成了数据是不一致的。当在表IC_WB_RELEASE发生delete操做时,引起了下面的故障,致使从节点脱离集群。数据库
2019-01-28T11:59:30.919880Z 6 [ERROR] Slave SQL for channel 'group_replication_applier': Could not execute Delete_rows event on table `world`.`IC_WB_RELEASE`; Can't find record in 'IC_WB_RELEASE', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND, Error_code: 10322019-01-28T11:59:30.919926Z 6 [Warning] Slave: Can't find record in 'IC_WB_RELEASE' Error_code: 10322019-01-28T11:59:30.920095Z 6 [ERROR] Plugin group_replication reported: 'The applier thread execution was aborted. Unable to process more transactions, this member will now leave the group.'2019-01-28T11:59:30.920165Z 6 [ERROR] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'FIRST' position 271.2019-01-28T11:59:30.920220Z 3 [ERROR] Plugin group_replication reported: 'Fatal error during execution on the Applier process of Group Replication. The server will now leave the group.'2019-01-28T11:59:30.920298Z 3 [ERROR] Plugin group_replication reported: 'The server was automatically set into read only mode after an error was detected.'
问题分析网络
对于该故障的分析,咱们要从主从实例GTID相同,可是事务不一样的缘由入手,该问题猜想与bug(https://bugs.mysql.com/bug.ph...)相关,咱们针对MGR同步事务的时序作以下分析。架构
相关知识背景app
MGR全组同步数据的Xcom组件是基于paxos算法的实现;每当提交新生事务时,主实例会将新生事务发送到从实例进行协商,组内协商经过后全组成员一块儿提交该事务;每个节点都以一样的顺序,接收到了一样的事务日志,因此每个节点以一样的顺序回放了这些事务,最终组内保持了一致的状态。
paxos包括两个角色:
提议者(Proposer):主动发起投票选主,容许发起提议。
接受者(Acceptor):被动接收提议者(Proposer)的提议,并记录和反馈,或学习达成共识的提议。
paxos达成共识的过程包括两个阶段:
第一阶段(prepare)
a:提议者(Proposer)发送prepare请求,附带本身的提案标识(ballot,由数值编号加节点编号组成)以及value值。组成员接收prepare请求;b:若是自身已经有了确认的值,则将该值以ack_prepare形式反馈;在这个阶段中,Proposer收到ack回复后会对比ballot值,数值大的ballot会取代数值小的ballot。若是收到大多数应答以后进入下一个阶段。
第二阶段(accept)
a:提议者(Proposer)发送accept请求
b:接收者(Acceptor)收到请求后对比自身以前的bollat是否相同以及是否接收过value值。若是未接受过value值 以及ballot相同,则返回ack_accept,若是接收过value值,则选取最大的ballot返回ack_accept。c:以后接受相同value值的Proposer节点发送learn_op,收到learn_op节点的实例表示确认了数据修改,传递给上层应用。
针对本文案例咱们须要强调几个关键点:
分析过程
结合paxos时序,咱们对案例过程进行推测:
Tips:如下分析过程请结合时序图操做步骤观看
建议点开放大,效果更清晰 ^ ^
其余的从实例因为收到过主节点的value值;因此将主节点的(ballot=0.0,value=world.IC_WB_RELEASE)返回;而收到的ack_prepare的ballot值的数值符号全组内被初始化为0,整个ballot的大小彻底由节点编号决定,因而从节点选取了ballot较大的该实例value值做为新的提案,覆盖了主实例的value值并收到大多数成员的ack_accept【step2】。并在组成员之间发送了learn_op信息【step3】,跳过了主实例提议的事务。
从源码中能够看到关于handle_ack_prepare的逻辑。
handle_ack_prepare has the following code: if (gt_ballot(m->proposal,p->proposer.msg->proposal)) { replace_pax_msg(&p->proposer.msg, m); ... }
▽过程总结:
结论
针对此问题咱们也向官方提交SR,官方已经反馈社区版MySQL 5.7.26和MySQL 8.0.16 中会修复,若是是企业版客户能够申请最新的hotfix版本。
在未升级 MySQL 版本前,若再发生此类故障,在修复时须要人工检查,检查切换时binlog中 GTID 信息与新主节点对应 GTID 的信息是否一致。若是不一致须要人工修复至一致状态,若是一致才能够将被踢出的原主节点加回集群。
因此正在使用了MGR 5.7.26以前社区版的DBA同窗请注意避坑。
开源分布式中间件DBLE
社区官网: https://opensource.actionsky....
GitHub主页: https://github.com/actiontech...
技术交流群:669663113
开源数据传输中间件DTLE
社区官网: https://opensource.actionsky....
GitHub主页: https://github.com/actiontech...
技术交流群:852990221