MySQL主从同步不一致遇到的问题以及解决方案
前言
为了更了解binlog日志的结构,于是使用mysqlBinlog工具查看binlog日志,其中选取的binlog日志量是从主库同步过来的,进行重放,导致本地执行的事务与主库的不一致;
笔者在主从同步的binlog格式使用的是mixed,也就是MySQL会判断同步的sql是否会引起主备不一致,当认为同步的sql不会引发不一致,就使用statement格式,否则使用row格式;
statement格式和row格式
statement格式
binlog记录的是执行的sql语句,主备同步使用该格式会 存在不一致问题,例如主库执行delete from t where a>20 and b<10 limit 10; binlog记录的就是该sql,删除时选取的是a索引,删除了主键为1,2的记录。而同步给备库时,因选取的是b索引,删除的是主键3,4的记录,导致主备不一致;
row格式
记录的不再是sql语句,而是两个Event事件语句,分别是Table_map_event(用于指定操作的表是哪个),Delete_rows_event(用于指定删除的行为,也会记录删除的那一行数据),优点是不会存在主备不一致问题;但更占存储空间;
如下图就是row格式的binlog:
begin和commit之间就是要删除的数据,两个Event事件用了CRC32函数进行计算
使用mysqlbinlog工具查看备库的binlog,并重放sql
如上面所说,binlog配置的是mixed格式,在主库执行了该sql
insert into t values(10,10, now());
然后在备库也能看到该sql生效了,但笔者手动删除了该sql,打算从binlog选定日志量恢复,因binlog是二进制,故使用mysqlbin -vv命令明文输出到另一个文件:
打开test.binlog文件如下图
看来MySQL认为该sql不会导致主备不一致,主动选择了statement格式,笔者就从中选择了insert into values(100,1,now())在备库进行重放执行(红圈圈住的语句先按下不表,各位看官请先往下看~)
结果:
笔者手动在主库和备库分别执行的now(),加上主备同步延迟的缘故,主键100这行的时间字段是不一样的,后来翻看主库同步过来的binlog日志,发现begin和commit之间,还有如上图2圈住的一行SET TIMESTAMP,也就就是主库同步过来的时间戳,如果笔者不是手动删除该行数据,然后重新执行却漏执行该时间戳语句,就不会导致主备中该行记录不一致;
解决方案以及建议:
删除从主库同步过来的binlog后,切忌直接将statement格式的语句拷贝出来直接执行,如果误删除后,应该找到最近的那一次全量复制的master_log_pos,以及执行删除语句之前的master_log_pos,然后执行
mysqlbinlog /var/lib/mysql/mysql-bin.000005 --start-position1673097 --stop-position=1673374 -vv > /tmp/mysql.binlog
明文输出到MySQL.binlog文件
最后使用source命令执行mysql.binlog文件即可恢复