天天看點

semisync continue…

在前兩天剛剛釋放的mysql5.7.2代碼中,可以看到semisync終于做了進一步的改進;在之前使用者線程的邏輯為:

1.在binlog寫入檔案後,調用semisync記錄事務位點資訊;

2.sync binlog(如果需要)

3.将事務送出;

4.等待備庫ack;

該邏輯存在的問題是,當主庫crash時,可能binlog還沒有傳遞到備庫,這時候做一次主備切換,備庫提升為新主庫,老主庫降級為新備庫,這時候主備庫是處于不一緻狀态的;

而在5.7.2中,可以使用如下邏輯:

1.在binlog寫入檔案後,記錄事務位點資訊;

2.sync binlog(如果需要)

3.等待備庫ack;

4..将事務送出;

在新版本5.7.2中,增加了新的hook函數:after_sync,通過參數rpl_semi_sync_master_wait_point來控制,預設為after_sync,如果設定為after_commit則在commit之前等待備庫ack,這可以確定備庫接受到了最新的binlog,即使主庫随後crash掉,也可以在recovery階段恢複,因為主庫已經完成了prepare階段;

盡管該特性僅存在于官方5.7.2中,使用mysql5.6的同學也可以很容易的把這個特性port過來,因為代碼的改動量非常小.但需要注意一個問題,即在rotate時,由于需要持有lock_log并等待所有preare的事務完成commit,而dump線程也需要持有lock_log來發送binlog,這裡rotate, 使用者線程等待ack, dump線程三者很容易形成死鎖。

解釋下,rotate為什麼要等待prepare的線程commit,由于我們預設使用的xa事務,在crash recovery時,會掃描最後一個binlog檔案,找出其中的xid,然後再跟所有處于prepare狀态的事務xid做對比,如果事務xid存在于binlog,則将該prepare的事務commit,否則rollback。

如果rotate時不等待prepare的事務commit,那麼我們可能需要往前掃描多個binlog檔案來

關于這個問題,有幾種解決辦法:

1.mariadb版本,rotate線程無需等待prepare線程commit,而是在binlog中寫入事件的方式,類似于給binlog做了個checkpoint。

2.mysql5.7.2/facebookmysql5.6, 通過維護目前active的binlog尾部偏移量的方式來解除dump線程對lock_log鎖的需求