MySQL 主從數據庫同步是如何實現的?

回顧我們之前講 MySQL 相關的幾節課程,你會發現 主從同步有多重要:

  • 解決數據可靠性的問題需要用到主從同步;
  • 解決 MySQL 服務高可用要用到主從同步;
  • 應對高併發的時候,還是要用到主從同步。

我們在運維 MySQL 集羣時,遇到的很多常見的問題,比如說:

  • 爲什麼從節點故障會影響到主節點?
  • 爲什麼主從切換之後丟數據了?
  • 爲什麼明明沒有更新數據,客戶端讀到的數據還是變來變去的?

這些都和主從同步的配置有密切的關係。

你不但要理解 MySQL 主從同步的原理,還要掌握一些相關配置的含義,才能正確地配置你的集羣,知道集羣在什麼情況下會有什麼樣的行爲,可能會出現什麼樣的問題,並且知道該如何解決。

今天這節課我們就來詳細講一下,MySQL 的主從同步是怎麼實現的,以及如何來正確地配置主從同步。

#如何配置 MySQL 的主從同步?

當客戶端提交一個事務到 MySQL 的集羣,直到客戶端收到集羣返回成功響應,在這個過程中,MySQL 集羣需要執行很多操作:

  • 主庫需要:
    • 提交事務
    • 更新存儲引擎中的數據
    • 把 Binlog 寫到磁盤上
    • 給客戶端返回響應
    • 把 Binlog 複製到所有從庫上
  • 每個從庫需要
    • 把複製過來的 Binlog 寫到暫存日誌中
    • 回放這個 Binlog
    • 更新存儲引擎中的數據
    • 給主庫返回複製成功的響應。

這些操作的時序非常重要,這裏面的 時序,說的就是這些 操作的先後順序。同樣的操作,因爲時序不同,對應用程序來說,有很大的差異。比如說,如果先複製 Binlog,等 Binlog 複製到從節點上之後,主節點再去提交事務,這種情況下,從節點的 Binlog 一直和主節點是同步的,任何情況下主節點宕機也不會丟數據。但如果把這個時序倒過來,先提交事務再複製 Binlog,性能就會非常好,但是存在丟數據的風險。

MySQL 提供了幾個參數來配置這個時序,我們先看一下默認情況下的時序是什麼樣的。

默認情況下,MySQL 採用異步複製的方式,執行事務操作的線程不會等複製 Binlog 的線程。具體的時序你可以看下面這個圖:

img

MySQL 主庫在收到客戶端提交事務的請求之後,會先寫入 Binlog,然後再提交事務,更新存儲引擎中的數據,事務提交完成後,給客戶端返回操作成功的響應。同時,從庫會有一個專門的 複製線程,從主庫接收 Binlog,然後把 Binlog 寫到一箇中繼日誌裏面,再給主庫返回複製成功的響應。

從庫還有另外一個 回放 Binlog 的線程,去讀中繼日誌,然後回放 Binlog 更新存儲引擎中的數據,這個過程和我們今天討論的主從複製關係不大,所以我並沒有在圖中畫出來。提交事務和複製這兩個流程在不同的線程中執行,互相不會等待,這是異步複製。

掌握了異步複製的時序之後,我們就很容易理解之前幾節課中講到的一些問題的原因了。比如說,在異步複製的情況下,爲什麼主庫宕機存在丟數據的風險?爲什麼讀寫分離存在讀到髒數據的問題?產生這些問題,都是因爲 異步複製它沒有辦法保證數據能第一時間複製到從庫上。

與異步複製相對的就是同步複製。同步複製的時序和異步複製基本是一樣的,唯一的區別是,什麼時候給客戶端返回響應。

  • 異步複製時,主庫提交事務之後,就會給客戶端返回響應;
  • 同步複製時,主庫在提交事務的時候,會等待數據複製到所有從庫之後,再給客戶端返回響應。

同步複製這種方式在實際項目中,基本上沒法用,原因有兩個:

  • 一是性能很差,因爲要複製到所有節點才返回響應;
  • 二是可用性也很差,主庫和所有從庫任何一個數據庫出問題,都會影響業務。

爲了解決這個問題,MySQL 從 5.7 版本開始,增加一種 **半同步複製(Semisynchronous Replication)**的方式。

  • 異步複製是,事務線程完全不等複製響應;
  • 同步複製是,事務線程要等待所有的複製響應;
  • 半同步複製介於二者之間,事務線程不用等着所有的複製成功響應,只要一部分複製響應回來之後,就可以給客戶端返回了。

比如說,一主二從的集羣,配置成半同步複製,只要數據成功複製到任意一個從庫上,主庫的事務線程就直接返回了。這種半同步複製的方式,它兼顧了異步複製和同步複製的優點。如果主庫宕機,至少還有一個從庫有最新的數據,不存在丟數據的風險。並且,半同步複製的性能也還湊合,也能提供高可用保證,從庫宕機也不會影響主庫提供服務。所以,半同步複製這種折中的複製方式,也是一種不錯的選擇。

接下來我跟你說一下,在實際應用過程中,選擇半同步複製需要特別注意的幾個問題。

配置半同步複製的時候,有一個重要的參數 rpl_semi_sync_master_wait_no_slave,含義是:「至少等待數據複製到幾個從節點再返回」。這個數量配置的越大,丟數據的風險越小,但是集羣的性能和可用性就越差。最大可以配置成和從節點的數量一樣,這樣就變成了同步複製。

一般情況下,配成默認值 1 也就夠了,這樣性能損失最小,可用性也很高,只要還有一個從庫活着,就不影響主庫讀寫。丟數據的風險也不大,只有在恰好主庫和那個有最新數據的從庫一起壞掉的情況下,纔有可能丟數據。

另外一個重要的參數是 rpl_semi_sync_master_wait_point,這個參數 控制主庫執行事務的線程,是在提交事務之前(AFTER_SYNC)等待複製,還是在提交事務之後(AFTER_COMMIT)等待複製。默認是 AFTER_SYNC,也就是先等待複製,再提交事務,這樣完全不會丟數據。AFTER_COMMIT 具有更好的性能,不會長時間鎖表,但還是存在宕機丟數據的風險。

另外,雖然我們配置了同步或者半同步複製,並且要等待複製成功後再提交事務,還是有一種特別容易被忽略、可能存在丟數據風險的情況。

如果說,主庫提交事務的線程等待複製的時間超時了,這種情況下事務仍然會被正常提交。並且,MySQL 會自動降級爲異步複製模式,直到有足夠多(rpl_semi_sync_master_wait_no_slave)的從庫追上主庫,才能恢復成半同步複製。如果這個期間主庫宕機,仍然存在丟數據的風險。

#複製狀態機:所有分佈式存儲都是這麼複製數據的

在 MySQL 中,無論是複製還是備份恢復,依賴的都是 全量備份和 Binlog,全量備份相當於備份那一時刻的一個數據快照,Binlog 則記錄了每次數據更新的變化,也就是操作日誌。我們這節課講主從同步,也就是數據複製,雖然講的都是 MySQL,但是你要知道,這種基於「快照 + 操作日誌」的方法,不是 MySQL 特有的。

比如說,Redis Cluster 中,它的全量備份稱爲 Snapshot,操作日誌叫 backlog,它的主從複製方式幾乎和 MySQL 是一模一樣的。

我再給你舉個例子,之前我們講過的 Elasticsearch,它是一個內存數據庫,讀寫都在內存中,那它是怎麼保證數據可靠性的呢?對,它用的是 translog,它備份和恢復數據的原理和實現方式也是完全一樣的。這些什麼什麼 log,都是不同的馬甲兒而已,幾乎所有的存儲系統和數據庫,都是用這一套方法來解決備份恢復和數據複製問題的。

既然這些存儲系統他們實現數據複製的方法是完全一樣的,那這幾節課我們講的 MySQL 主從複製時,講到的那些問題、丟數據的風險,對於像 Redis Cluster、ES 或者其他分佈式存儲也都是一樣存在的。那我們講的,如何應對的方法、注意事項、最佳實踐,這些也都是可以照搬的。

這一套方法其實是有理論基礎的,叫做 複製狀態機 (Replication State Machine) (opens new window),我能查到的最早的出處是 1978 年 Lamport 的一篇論文《The Implementation of Reliable Distributed Multiprocess Systems》 (opens new window)

1978 年啊,同學,那時候我們都還沒出生呢!這麼老的技術到今天仍然在被廣泛地應用!無論應用技術發展的多快,實際上解決問題的方法,或者說是理論基礎,一直是沒什麼變化的。所以,你在不斷學習新的應用技術的同時,還需要多思考、總結和沉澱,這樣會讓你學習新技術的時候更快更輕鬆。

#小結

最後,那爲了便於你理解複製狀態機,我們把這套方法再抽象總結一下。任何一個存儲系統,無論它存儲的是什麼數據,用什麼樣的數據結構,都可以抽象成一個狀態機。

存儲系統中的數據稱爲狀態(也就是 MySQL 中的數據),狀態的全量備份稱爲快照(Snapshot),就像給數據拍個照片一樣。我們按照順序記錄更新存儲系統的每條操作命令,就是操作日誌(Commit Log,也就是 MySQL 中的 Binlog)。你可以對照下面這張圖來理解上面這些抽象的概念。

img

複製數據的時候,只要基於一個快照,按照順序執行快照之後的所有操作日誌,就可以得到一個完全一樣的狀態。在從節點持續地從主節點上覆制操作日誌並執行,就可以讓從節點上的狀態數據和主節點保持同步。

主從同步做數據複製時,一般可以採用幾種複製策略。性能最好的方法是異步複製,主節點上先記錄操作日誌,再更新狀態數據,然後異步把操作日誌複製到所有從節點上,並在從節點執行操作日誌,得到和主節點相同的狀態數據。

異步複製的劣勢是,可能存在主從延遲,如果主節點宕機,可能會丟數據。另外一種常用的策略是半同步複製,主節點等待操作日誌最少成功複製到 N 個從節點上之後,再更新狀態,這種方式在性能、高可用和數據可靠性幾個方面都比較平衡,很多分佈式存儲系統默認採用的都是這種方式。

#思考題

複製狀態機除了用於數據庫的備份和複製以外,在計算機技術領域,還有哪些地方也用到了複製狀態機?歡迎你在留言區與我討論。

答:複製狀態機的應用是非常廣泛的,比如說現在很火的區塊鏈技術,也是借鑑了複製狀態機理論,它的鏈,或者說是賬本就是操作日誌,每個人的錢包,就是狀態。它只要保證賬本一旦記錄後就不會被篡改,那在任何人的電腦上,計算出來的錢包就都是一樣的。

  • redis 中也有類似的:RDB 快照、AOF 日誌
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章