MySQL 內建的復制功能是構建基于 Mysql 的大規模、高性能應用的基礎,同時也是高可用性、可擴展性、災難恢復、備份及數據倉庫等工作的基礎。通過本章內容,可以更好地理解主從復制的實現機制。
復制解決的問題
- 高可用,避免單點問題,主庫出現故障時,應用依然可以從從庫查詢數據,不影響查詢業務,另外通過故障切換功能,可以將從庫切換為主庫,縮短系統宕機時間。
- 數據備份,數據在系統中存在多份,其中一個機器出現故障,還可以從其它機器獲取數據及修復數據,但是復制不能取代備份。
- 負載均衡,應用系統可以通過讀寫分離實現分流,減輕數據庫訪問壓力。
- 升級測試,需要升級 Mysql 版本時,可以先升級從庫的版本,保證查詢業務可以在高版本的從庫中正常運行,之后再全面升級所有實例。
復制方式
Mysql 支持兩種復制方式:基于語句的復制 和 基于行的復制。兩種方式都是通過在主庫上記錄二進制日志,在從庫重放日志的方式來實現異步的數據復制。這意味著,同一時間點,從庫可能與主庫的數據不一致,并且無法保證延遲時間,一些大的語句可能導致從庫產生幾秒、幾分鐘甚至幾小時的延遲。
- 基于語句的復制
主庫會將造成數據變更的語句記錄到二進制日志,然后到從庫重放,本質上就是把主庫執行過的語句到從庫再執行一遍。實現簡單、事件緊湊,占用帶寬小。
- 基于行的復制
主庫會將實際數據記錄到二進制日志,由于無須重放更新主庫數據的語句,基于行的復制模式能夠更高效地復制數據。
有的語句執行開銷比較大,如下面的語句,最終只在目標表上增加三條記錄,如果使用基于行的復制模式,在從庫上開銷就小很多。
insert into summary(col1, col2, col3)
select col1, col2, sum(col3)
from enormous
group by col1, col2;
下面的語句,進行了全表更新,使用基于行的復制模式開銷就很大,因為每一行的數據都會被記錄到二進制日志,這使得二進制日志非常龐大,占用大量磁盤空間。
update enormous set cole = 0;
理論上基于行的復制整體上更優。

復制工作原理
1、主庫把數據更新記錄到二進制日志(Binary Log)中。
每次提交事務完成數據更新前,主庫將數據更新的事件記錄到二進制日志中。Mysql 會按事務提交順序而非每條語句的執行順序來記錄二進制日志。記錄完二進制日志后,主庫會通知存儲引擎執行事務提交。
2、從庫將主庫上的日志復制到從庫的中繼日志(Relay Log)中。
從庫啟動一個工作線程,稱為IO線程,IO線程與主庫建立一個普通的客戶端連接,然后在主庫上啟動一個特殊的二進制轉儲(binlog dump)線程,這個二進制轉儲線程會讀取主庫上二進制日志中的事務,但它不會對事件進行輪詢。如果該線程追趕上了主庫,它將進入睡眠狀態,直到主庫發送信號量通知其有新的事件產生時才會被喚醒,從庫IO線程會將接收到的事件記錄到從庫的中繼日志中。
3、從庫讀取中繼日志中的事件,將其重放到從庫數據之上。
從庫啟動一個SQL線程,讀取中繼日志中的事件并在從庫執行,實現從庫數據更新。當SQL線程追趕上IO線程時,中繼日志通常已經在系統緩存中,所以中繼日志的開銷很低。
具體工作流程如下圖所示:

這種架構實現了獲取事件與重放事件的解耦,允許這兩個過程異步執行,也就是說 IO 線程可以獨立于 SQL 線程之外工作。
多級復制
log_slave_updates 選項可以讓從庫變成其它服務器的主庫,該選項默認是打開的,這樣就會形成級聯的復制。
具體工作流程如下圖所示:

結束語:
Mysql 主從復制并沒有使用輪詢的方式從主庫請求事件,而是主庫通知從庫新的事件,因為前者低效且緩慢,從主庫讀取一個二進制日志事件是一個阻塞型網絡調用,當主庫記錄事件后,馬上就開始發送,因此可以說,只要復制線程被喚醒并且能夠通過網絡傳輸數據,事件就會很快到達從庫,大多數小查詢在主庫的執行時間與在備庫的執行時間間隔小于 0.3 毫秒。