主庫宕機不丟數據(Master Failover without data loss)php
半同步複製實現的關鍵點是Master對於事務提交過程特殊處理。目前實現半同步複製主要有兩種模式,AFTER_SYNC模式和AFTER_COMMIT模式。兩種方式的主要區別在因而否在存儲引擎提交後等待Slave的ACK。git
下面展現了半同步複製中,binlog的提交過程。sql
1. binlog prepare (doing nothing) 2. innodb prepare (fsync) 3. binlog commit (writing to fscache) 4. binlog commit (fsync) 5. loss-less semisync wait (AFTER_SYNC) 6. innodb commit (releasing row locks, changes are visible to other users) 7. normal semisync wait (AFTER_COMMIT)
半同步複製是否能保證不丟數據?安全
咱們經過幾種場景來簡單分析下。網絡
第一種狀況:假設Master前4步binlog commit執行成功後,binlog還沒來得及傳遞給Slave,此時Master掛了,Slave做爲新Master提供服務,那麼備庫比主庫要少一個事務(由於主庫的redo 和binlog已經落盤),可是不影響用戶,對於用戶而言,這個事務沒有成功返回,那麼提交與否,用戶均可以接受,用戶必定會進行異常捕獲而重試。less
第二種狀況,假設innodb commit執行成功後,binlog還沒來得及傳遞給Slave,此時Master掛了,此時與第一種狀況同樣,備庫比主庫少一個事務。以下圖所示,在AFTER_COMMIT模式下,user1在innodb commit執行完後,其餘用戶能夠看到該事務的更新,而切換到備庫後,卻發現再次讀這個更新又沒了,這個就發生了「幻讀」,若是其餘事務依賴於這個更新,則會對業務邏輯產生影響。固然這僅僅是極端狀況。gitlab
AFTER_SYNC模式能夠解決「幻讀」問題。master在AFTER_SYNC模式下,Fsync binlog後,就開始等待Slave同步。那麼在進行innodb commit後,即其它事務能看到該事務的更新時,Slave已經成功接收到binlog,即便發生切換,Slave擁有與Master一樣的數據,不會發生「幻讀」現象。可是對於上面描述的第一種狀況,結果是同樣的。性能
因此,在極端狀況下,半同步複製的Master-Slave會有一個事務不一致,可是對於用戶而言,因爲這個事務並無成功返回給用戶,因此不管事務提交與否都是能夠接受的,用戶有必要進行查詢或重試,判讀是否更新成功。或者咱們想一想,對於單機而言,若事務執行成功後,返回給用戶時,網絡斷了,用戶也是面臨同樣的問題,因此,這不是半同步複製的問題。對於提交返回成功的事務,版同步複製保證Master-Slave必定是一致的,從這個角度來看,半同步複製不會丟數據,能夠保證Master-Slave的強一致性。
之前mysql5.6有個Bug: 主庫在(2)(3)之間宕機,接着主庫故障恢復後,主備之間的複製會中斷,備庫會報1206的錯。
(1)master writes to binlog (writing to kernel buffer) (2)binlog dump threads read the binlog events and send to slaves (3)master flushes to binlog (fsync to binlog file)
爲何會有這個Bug產生呢?緣由是在5.6僅僅只是在writing to kernel buffer階段持有LOCK_log鎖。因此在 fsync()完成以前,binlog dump線程就能夠讀取主庫的binlog,發送到備庫去。
爲了修復這個bug,主庫增長了持有LOCK_log鎖的時間,直到fsync()結束後釋放。這個改進點退化了半同步複製的性能。由於在5.6中,LOCK_log鎖是一個很是熱的mutex鎖。binlog dump線程和用戶線程都須要去持有LOCK_log鎖。
不過比較好的是,將持久化參數設置成非嚴格模式(sync_binlog=0;innodb_flush_log_at_trx_commit=0|2),能夠緩解LOCK_log鎖帶來的性能退化。
對於LOCK_log鎖的優化,能夠看看這個連接:
http://my-replication-life.blogspot.com/2013/09/dump-thread-enhancement.html
http://www.actionsky.com/docs/archives/129
在主庫上,binlog的寫入和讀取都須要同一把鎖來保護,也就是LOCK_log,當寫入負載較大時,LOCK_log成爲熱點鎖;而對於dump線程而言,每個dump線程在讀取binlog事件時,都須要先持有LOCK_log鎖;dump線程越多,引發的競爭越激烈。
當dump線程沒法及時獲取LOCK_log鎖時,就會影響發送binlog到備庫的速率,進而影響備庫IO線程返回ACK的速率。
拆分的思路也很簡單,就是每次寫入binlog時,維持該binlog文件末尾的偏移量;在該偏移量以前咱們均可以安全讀取binlog文件而無需加鎖。
首先區分一點,備庫的LOCK_log屬於relay log,和主庫的LOCK_log屬於不一樣的類對象。 備庫上,SQL線程與IO線程在一種狀況下會存在LOCK_log競爭,也就是當前SQL線程執行的relylog和IO線程寫入的relaylog是同一個文件時,這時候IO線程和SQL線程使用的是同一個IO CACHE來操做文件,所以必須使用LOCK_log來保證讀和寫的互斥;
爲了分拆LOCK_log,須要實現以下兩點: a.SQL線程老是在讀取事件時,使用自有的IO CACHE,而不是和IO線程公用IO CACHE b.和主庫LOCK_log拆分相似,須要在IO線程寫入relay log時,維持文件末尾偏移量,SQL線程能夠根據該偏移量安全的讀取事件