MySQL复制可能造成数据不一致的地方
上周在讲复制故障处理,利用DML在从主上手工造数据,导致主从复制中断,然后处理复制故障,同时给大家安利了:
课程Demo程序:主从故障自动修复:zhishutech/ReplGurd (github.com) 建议fork|star | watch 有完善的地方请pull回来
复制主从数据一致性校验、修复程序:pt-table-checksum/pt-table-sync 使用。
结课QA环节一个学生问到:老师除了误操作写了从节点造成数据不一致性外,还有哪些原因?
看到这个问题,当时我真的是一口鲜血喷在了屏幕上啊?
在4月26日MySQL复制原理及应用中刚讲了复制原理及半同步中可能出现的数据不一致时间点,整整用了一节课,在5月10日课中,被问到这个问题。有点无语了,老师也想说你们是我见过最差的一届了,怎么刚讲过,你们都忘了呢~~~ :) 好吧。当时就利用老师的特权给你们留个作业,回顾:MySQL复制原理及应用场景,试试能不能解答复制主从可能造成主从数据不一致的地方。果真有很给力的同学,不管什么是哪一届都还是有很多优秀的同学,第二天一早就收到一份作业,也分享出来,给各位一个参考:
主从复制可能造成不一致分析(作者A1364-路遥-北京)
MYSQL5.7 之前半同步复制采用的是 AFTER_COMMIT 方式--比 AFTER_SYNC 会有更大概率造成数据不一致
AFTER_COMMIT 是先做 REDO COMMIT 后传 BINLOG,做事务提交,只是不给客户端返回。
AFTER_COMMIT模式下丢失数据实验
版本8.0.23 (版本不重要,原理没变,所有MySQL都一样,本期课程使用的MySQL 8.0.23)
主库参数
+-------------------------------------------+--------------+
| Variable_name | Value |
+-------------------------------------------+--------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 60000000000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_COMMIT |
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+-------------------------------------------+--------------+
从库参数
+-------------------------------------------+--------------+
| Variable_name | Value |
+-------------------------------------------+--------------+
| rpl_semi_sync_master_enabled | OFF |
| rpl_semi_sync_master_timeout | 60000000000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_COMMIT |
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+-------------------------------------------+--------------+
从库上停掉IO_THREAD模拟从库异常
stop replica io_thread;
主库上插入一条数据,此时会HANG住(但是这条数据已经写入了,开启一个会话是可以查到该数据的)
insert into t values(1);
开启新SESSION查询T表
select * from t;
+------+
| id |
+------+
| 1 |
+------+
开启另一个会话杀掉主库MYSQLD进程pkill -9 mysqld
此时从库中是查不到这条数据的。
select * from t;
Empty set (0.00 sec)
如果此时发生主从切换则主从数据发生不一致。这也是after_commit模式复制中幻读现象。 如图:
AFTER_SYNC 是先传 binlog 后做 REDO COMMIT
```
极端有两种情况:
1. 当主库还没来的及把日志传输到从库上;主库上在完成write binlog后crash
2.日志已经传输到从库上,完成了wait slave ack,此时发生crash;应用端此时并没有接收到主库返回OK。
情况1. 主库Crash恢复后,这个事务操作数据可以被commit,这种事务可以称为local commit或是幽灵事务,并没有真正的完成半同步。
情况2. 产生脏数据,是一个业务没得到确认的事务。也可以称为幽灵事务。
在after_sync中,确实不会丢数据了,但有可能会多数据。
那么使用复制如何保证数据的绝对一致性呢?
1. 首先这个需求是假的,不存在的,如果想要绝对的一致目前可以考虑MySQL Group Replication。
2. 再次,如果一定要用复制架构,同时又要绝对的一致性,考虑使用增强半同步结合session_track_gtids功能使用。
3. 复制一定是binlog row格式+gtid,同时在数据库故障时,注意local commit问题,引入数据校验机器。
孔子曰:三人行,必有我师。大胆交流,不耻下问。加油,学到了才是真本事。欢迎交流。
本文使用 文章同步助手 同步