天天看点

MySQL · 捉虫动态 · 并行复制外键约束问题二

一般情况的复制是 a->b 这样一主一备,本文要描述的场景是 a->b->c 这样一主两备,并且备库级联,其中备库 c 开启了并行复制,b 可以串行也可以并行,binlog_fomat 都是 row。

在主库a上执行如下语句:

备库 c 上会报错如下,非常明显的一个外键约束的错误:

这个机制是没问题的,如果 flag 能从 a 传到 b 再传到 c,就不会出现这个问题,现在问题的出现是因为备库 b 执行完父表(parent)的更新后,写 binlog 时 flag 没写进去,导致 c 在并行模式下执行 parent 表更新时,没有切换到串行模式,和 child 表的更新同时在跑,如果执行 child 表更新的 worker 先做,那么就会出现外键约束报错。

<code>tm_referred_fk_db_f</code> 这个 flag 是在 <code>table_map_log_event::table_map_log_event()</code> 构造函数中设置的,逻辑如下:

如果当前访问到的 db 个数为1,并且 db 是空字符串 <code>""</code> 的话,就设置这个 flag。<code>binlog_accessed_db_names</code> 中只有<code>""</code> 这一个元素是一个特殊构造的场景,正常情况下db不会是 <code>""</code>的,构造这样 db 的逻辑在 <code>thd::decide_logging_format</code>,如下:

可以看到,如果有当前表被外键约束的话(<code>table-&gt;table-&gt;file-&gt;referenced_by_foreign_key()</code>),会清掉<code>binlog_accessed_db_names</code>,只放一个空字符串进去。

但是 sql 线程在应用 row_event 时,不会走到上面的逻辑,因为 <code>lex-&gt;sql_command</code> 的值为 <code>sqlcom_end</code>,所以备库 b 生成的 parent 表的 table_map 就不包含这个 flag。

继续阅读