然而最近线上重放binlog时遇到了这样一个错误:

    查看对应 binlog 的,发现这是一个 CREATE VIEW 语句,而备份集恢复出来的实例上确实已经有了这个view,再往前翻看binlog,并没有发现 DROP 这个 view 的记录,倒是找到了CREATE 这个 view 的记录,仔细比较2处 CREATE VIEW 的binlog event,会发现后者多了个 error_code=1050,这个是什么错呢:

    1050 对应的错就是 Table already exists

    复现非常简单,连着执行同一个create view语句即可。

    查看binlog event

    可以清楚的看到,第二次 CREATE VIEW 时error_code 为 1050。

    正常的想法应该是执行出错,就不应该记binlog,为什么会有这样的设计呢,主库错,记binlog,然后备库要求同样的错。 因为DDL是不能回滚的,如果DDL执行到一半报错,主库又不能回滚,那么应该如何通知备库它做了一半呢?就是把错记下去,期待备库也报同样的错。

    挖一下黑历史,Query_log_event 中的 字段最早是在这个中加入的,目的是将主库上执行出错的信息传给备库,备库执行的时候会检测实际的出错信息和主库传过来的binlog中记录的是否是一样的,不一样就报错。

    在此之前,备库对于 Query_log_event 执行出错是这样处理的,先检查SQL线程执行出错是不是因为表不存在,如果是的话,就单独再开个连接,从主库把不存在的表导过来(),然后再重试执行失败的event,如果还有不存在的表,就再拉,再重复执行;对于其它的错就直接报错。 现在看起来是不是很奇葩,2000年的时候,MySQL还是很年青的哇 =_=

    所以如果脚本或者代码里有这种重放binlog逻辑的,需要注意处理这种场景。