天天看點

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。

繼續閱讀