分类: Mysql/postgreSQL
2013-02-27 12:29:03
经过分析最终找到了问题的原因,是由于server_id引起的,把上述复制构架进行简化可以很容易的重现事故
事故重现:
1、配置server1和server2为双主结构:server1(server_id=1) <–(dual master)–> server2(server_id=2)2、在server1执行以下SQL命令:mysql> create table test.t1(id int);mysql> stop slave;mysql> insert into t1 values(1);mysql> set global server_id=3;mysql> start slave;3、在server1,server2上执行:mysql> select count(*) from t1;
因此,对于上述简化结构,server1上插入一条记录一条记录之前stop slave,保证了插入查找在server2上执行后不能传到server1,在这之间server1的server_id发生了改变,当它start slave后,server2的复制事件被接收,由于server1此时的server_id为3,与复制事件中server_id(=1)不同,于是执行更新并写binlog,之后又将复制事件传递给server2,server2的server_id为2,也与复制事件中server_id(=1)不同,于是复制事件会在server_1和server_2之间循环传递和执行,停止这种传递的方法是将server1的server_id改回1
现在,线上事故也就不难解释了,MySQL1发送给MySQL2的复制事件,由于其server_id与MySQL2和MySQL3都不一样,当MySQL2与MySQL3互为双主后,复制事件会在它们之间循环传递和执行,让MySQL3的server_id变为MySQL1原来的server_id即可解决此问题(其实,这时MySQL3充当的就是MySQL1最初的角色)
此类问题一旦发生,大部分情况让MySQL主备表现异常,而少数情况会非常隐蔽,而如果没能及时发现并解决问题,数据就悲剧了…
因此,在到问题的真正原因后,我们尝试监测此问题,要想彻底杜绝不太可能,MySQL3自动判断它将要充当MySQL1的角色?
解决方案:
线上部署的MySQL节点拓扑结构有个明显特征:绝多数的1主1备,以及个别的1主多备,针对这些拓扑结构,处理此问题就可以简化许多,在备库复制的IO线程接收到主库发送的事件做如下判断:
其中:条件1和2确定此复制事件不是由自身或者其slave发出,条件3保证该复制事件有可能循环传递
当满足上述则可条件时,判定复制事件是经回路传送过来的,运行效果:
当MySQL复制的IO线程检测到事件存在回路时,会打印如下警告信息:
Detect dual master circular topological structure …然而,对于多个(>=3)节点构成的环形结构(生产环境基本不会使用),例如 M1-> S2 -> S3 -> M1, 上述判断无法起作用
社区反馈:
Percona的Sterward接受此问题 并将其标记为Medium,优先级中等的task,
1、server1:row,server2:statementserver1和server2上都只会执行一次insert(和正常情况一样)2、server1:statement,server2:rowserver1会执行两次insert,而server2只会执行1次3、server1和server2日志模式相同事件循环传递并被不断执行
原因也被找出:row模式日志无法重写成statement模式,而statement模式日志执行后可以写成row模式