本文將回顧MySQL復制概念和MySQL幾種復制方案,同時也會澄清一些關于復制問題的誤解。

MySQL復制是什么?
復制能夠保護信息得到備份,并且備份會不同于原數據,備份會被保存到另一個環境。即主備數據不在同一臺服務器。下圖是MySQL復制示意圖:

MySQL有 哪 些復制方案?
復制如此重要,那么在MySQL中我們有哪些選擇呢?
1. 異步復制
異步復制意味著本地環境操作完成,事務就完成 ,不會受到slave復制是否完成的影響。
當改變被提交后,master就會把數據修改信息放到binlog中(也可能把實際的statement放到binlog中,這是row-based復制和statement-based復制的不同,后面會講到)。dump線程讀取binlog日志然后將其發送到slave,slave接受并保存到待處理隊列中(被稱為relay-log),slave會執行每一個在master上的改變:

2. 半同步復制
半同步復制(Semi-synchronous replication)意味著master和slave彼此通訊確保事務的正確轉移。 當改變發生時,master需要等待slave已經將日志保存到relay-log中并向master回復確認master才能提交事務 。 半同步復制可以保證事務被正確的復制,但不能保證在slave上一定發生:

需要注意的是,半同步復制的話,master需要等待至少有一個slave服務器確認接收到事務并保存了relay-log(或達到超時)才能繼續處理同一個SESSION中的當前事務,至于具體多少個slave確認,可以通過參數rpl_semi_sync_master_wait_slave_count進行配置,這個值有效范圍是1~1024之間,而且這個值可以動態更新。舉個栗子:
假設rpl_semi_sync_master_wait_slave_count被設置為2,并且有兩個slave,分別是: slave1和slave2。
1. T1等待兩個slave的ack;
2. master收到slave1的ack;
3. 此時加入一個slave3,并將rpl_semi_sync_master_wait_slave_count改為3;
4. master收到 sl av e2的ack ;
5. master收到 sl av e3的ack;
6. master喚醒等待中的事務,并準備提交;
記住半同步復制會影響性能,因為它需要等待來自slave的確認(ack)。但是,它也能減少slave上由于故障導致數據丟失的風險。
3. 組復制
組(Group)復制是MySQL5.7版本新介紹的概念,在5.7.17發布了GA,而且以插件模式提供。
任意一個數據庫節點無論什么時候執行一個事務,組復制插件在向客戶端響應它已經完成事務前,會嘗試得到其他數據庫節點的同意。組復制示意圖如下:

4. Percona XtraDB Cluster / Galera Cluster
再介紹一個把master數據復制到其他節點的解決方案,就是Percona XtraDB Cluster,簡稱PXC。這個解決方案把重心放在一致性上,并且通過使用一個認證過程來保證事務避免沖突和執行的正確性。在這個集群方案的數據庫環境下, 每個節點的數據都是相同的,節點之間會依賴galera提供的廣播機制來保證一致性 。
以一條SQL為例,某節點接收SQL請求后,在commit之前,由wsrep API 調用galera庫進行集群內廣播,所有其他節點驗證成功后事務在集群所有節點進行提交,反之rollback。PXC保證整個集群所有數據的強一致性,滿足CAP理論中的CA,即 Consistency 和 Availability。
Percona XtraDB Cluster 有很多組件:
- Percona Server for MySQL(MySQL的Percona分支);
- Percona XtraBackup (主要用于集群的快照備份);
- wsrep patches / Galera Library;
該解決方案幾乎是同步的,可與組復制相媲美。但是,它還具有使用多主復制的能力。Percona XtraDB Cluster這樣的組件能很好的提高數據庫基礎架構可用性:

Row-Based VS. Statement-Based
討論MySQL復制,就不得不提Row-Based復制和Statement-Based復制。 因為它們是兩種不同復制方案的實現原理。
對于statement-based 復制(被翻譯為基于語句復制),執行的SQL本身會被寫入binlog中,例如完全相同的 INSERT/UPDATE/DELETE 語句會被在slave上執行。它的優缺點如下:
- 審計數據庫會更容易,因為實際執行的SQL語句就被記錄在binlog中;
- 主從之間會有更少的數據傳輸;
- binlog日志需要的空間更小;
- 不確定性SQL可能會給slave帶來很大的影響;
- 某些操作(例如insert...select)會有性能劣勢;
- Statement-based復制會由于SQL優化和執行變得更慢;
- slave上一些復雜SQL執行時,執行計劃評估可能變得糟糕;
- 數據一致性問題會有更大的挑戰;
Row-based 復制(基于行復制)是從MySQL 5.7.7起默認的選擇,它有很多優點,改變的行會被記錄在binlog中,因此它不需要上下文信息。它的其他優點如下::
- 每一個改變都能被復制,所以是最安全的復制方式;
- 對于包含不是很多行改變的高并發操作,性能有一定的提升;
- 顯著的改善了數據一致性;
當然,也有缺點:
- 網絡流量顯著變大,尤其當操作很多行記錄的時候,可能打爆網絡;
- 對于影響很多行的操作,Row-based就會很吃力,不擅長;
- 數據庫操作審計變得更加困難,因為binlog中不記錄SQL,取而代之的是記錄變更的數據;
- Row-based相比statement-based在一些場景下會更慢;
關于復制的誤解
誤解1: 復制就是集群
標準的異步復制不是集群,記住不管是標準的異步復制還是半同步復制,都不能保證環境服務于同一數據集。 而使用集群(例如Percona XtraDB Cluster)時,這是不同的,任意一個請求打到任意一臺服務器上其結果都一樣。如果不是,則會從群集中刪除受影響的節點。異步復制沒有這樣的保障,即使某個slave節點與master處于不一致狀態時仍然會接受操作請求。
誤解2: 復制作為手動故障轉移方案
理論上來說,兩個環境之間是有可比性的。然而,有許多參數能影響性能和數據一致性。只要你使用了異步復制,在master上發生的事務正確性就無法在slave上得到保障。當然,你可以通過增強持久化的配置來改善這點,但是它相應的會帶來一定的性能損耗,性能和可靠之間總需要做一定的取舍。
誤解3: 我有復制,所以不用備份
復制是為了對數據集有一個可訪問的副本的解決方案,通過把讀請求打到復制節點,能減輕master的壓力。但是復制不是備份。備份一般是指離線備份,它的作用是數據庫所在環境發生災難性的破壞不可恢復時,還能通過備份恢復數據庫。對數據庫進行離線備份是非常重要和有意義的事情!
誤解4: 因為有復制,所以數據庫能負載均衡事務請求
給master增加一個slave盡管可以改善系統的可用性,但是你仍然需要自己做幾件事情:把讀請求打到slave上,把寫請求打到master上。有很多代理工具可以完成這樣的事情,當然你可以自己動手造輪子!