MySql 主從同步過程知識講解

01 MySql 主從同步概述

在今年的敏捷團隊建設中,我通過 Suite 執行器實現了一鍵自動化單元測試。Juint 除了 Suite 執行器還有哪些執行器呢?由此我的 Runner 探索之旅開始了!

      MySQL 主從同步,即 MySQL Replication,可以實現將數據從一臺數據庫服務器同步到多臺數據庫服務器。MySQL 數據庫自帶主從同步功能,經過配置,可以實現基於庫、表結構的多種方案的主從同步。

      Redis 是一種高性能的內存數據庫,但不是今天的主角;MySQL 是基於磁盤文件的關係型數據庫,相比於 Redis 來說,讀取速度會慢一些,但是功能強大,可以用於存儲持久化的數據。在實際工作中常將 Redis 作爲緩存與 MySQL 配合來使用,當有數據訪問請求的時候,首先會從緩存中進行查找,如果存在就直接取出,如果不存在再訪問數據庫,這樣就提升了讀取的效率,也減少了後端數據庫的訪問壓力。使用 Redis 這種緩存架構是高併發架構中非常重要的一環。

圖 1 通常 Redis 使用示例圖

      隨着業務量的不斷增長,數據庫的壓力會不斷變大,緩存的頻繁變更也強依賴於數據的查詢結果,導致數據查詢效率低,負載很高,連接過多等問題。對於電商場景來說,往往存在很多典型的讀多寫少場景,可以通過對 MySQL 做主從架構並且進行讀寫分離,讓主服務器(Master)處理寫請求,從服務器(Slave)處理讀請求,進一步提升數據庫的併發處理能力。如下圖:

圖 2 讀寫分離場景示例

      從上圖中可以看到,增加的 2 個從庫可以一起抗下大量的讀請求,分擔主庫壓力。從庫會通過主從複製,從主庫中不斷的同步數據,以此來保證從庫的數據和主庫數據的一致。

      接下來將講解主從同步有哪些作用,以及主從同步具體是怎麼實現的。

02  主從同步的作用

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

      一般來說,不是所有的系統都需要對數據庫進行主從架構的設計,因爲架構本身是有一定成本的,如果目的在於提升數據庫高併發訪問的效率,那麼首先應該優化 SQL 語句及索引,充分發揮數據庫的最大性能;其次是採用緩存的策略,如使用 Redis、Magodb 等緩存工具,通過其高性能的優勢把數據保存在內存數據庫中,提升讀取的效率,最後對數據庫採用主從架構,進行讀寫分離。系統的使用和維護成本是根據架構的升級逐漸升高的。

      主從同步不僅可以提升數據庫的吞吐量,還有以下三個方面的作用:

2.1  讀寫分離

       通過主從複製的方式來同步數據,然後通過讀寫分離提升數據庫的併發處理能力。簡單來說就是數據放在多個數據庫中,其中一個是 Master 主庫,其餘的是 Slave 從庫。當主庫數據變化時,會自動將數據同步到從庫中,而程序可以從從庫讀取數據,也就是採用讀寫分離的方式。電商的應用往往是 “讀多寫少”,採用讀寫分離就實現了更高的併發訪問。原本所有的讀寫壓力都由一臺服務器承擔,現在有多個服務器共同處理讀請求,減少了對主庫的壓力。另外還可以對從服務器進行負載均衡,讓不同的讀請求按照策略均勻的分配到不同的從服務器中,讓讀取更加順暢。讀取順暢的另一個原因是減少了鎖表的影響,比如讓主庫負責寫,當主庫出現寫鎖的時候,不會影響到從庫的查詢操作。

2.2  數據備份

       主從同步也相當於是一種數據熱備份機制,在主庫正常運行下進行備份,不影響提供數據服務。

2.3  高可用性

      數據備份實際是一種冗餘的機制,通過這種冗餘的方式可以換取數據庫的高可用性,當服務器出現故障、宕機等無可用的情況下,可以迅速進行故障切換,讓從庫充當主庫,保障服務正常運行。讀者們可以瞭解下電商系統數據庫高可用 SLA 指標。

03  主從同步的原理

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

      說到主從同步的原理就需要了解在數據庫中的一個重要日誌文件,即 Binlog 二進制文件,它記錄了對數據庫進行更新的事件,事實上主從同步的原理就是基於 Binlog 進行數據同步的。

     在主從複製的過程中,會基於三個線程來操作,一個是 binlog dump 線程,位於 master 節點上,另外兩個線程分別是 I/O 線程和 SQL 線程,它們都分別位於 slave 節點上,如下圖:

圖 3 一般主從複製示例圖

結合上圖瞭解主從複製的核心流程:

  1. 當 master 節點接收到一個寫請求時,這個寫請求可能是增刪改操作,此時會把寫請求的更新操作都記錄到 binlog 日誌中。

  2. master 節點會把數據複製給 slave 節點,如圖中的 slave01 節點和 slave02 節點,這個過程首先得要每個 slave 節點連接到 master 節點上,當 slave 節點連接到 master 節點上時,master 節點會爲每一個 slave 節點分別創建一個 binlog dump 線程,用於向各個 slave 節點發送 binlog 日誌。

  3. binlog dump 線程會讀取 master 節點上的 binlog 日誌,然後將 binlog 日誌發送給 slave 節點上的 I/O 線程。當主庫讀取事件的時候,會在 Binglog 上加鎖,讀取完成之後,再將鎖釋放掉。

  4. slave 節點上的 I/O 線程接收到 binlog 日誌後,會將 binlog 日誌先寫入到本地的 relaylog 中,relaylog 中就保存了 binlog 日誌。

  5. slave 節點上的 SQL 線程,會來讀取 relaylog 中的 binlog 日誌,將其解析成具體的增刪改操作,把這些在 master 節點上進行過的操作,重新在 slave 節點上也重做一遍,達到數據還原的效果,這樣就可以保證 master 節點和 slave 節點的數據一致性了。

      主從同步的數據內容其實是二進制日誌(Binlog),儘管叫二進制日誌,實際上存儲的是一個又一個的事件(Event),這些事件分別對應着數據庫的更新操作,比如 INSERT、UPDATE、DELETE 等。(本文不對 Binlog 進行展開,對 Binlog 感興趣的讀者可以到官網詳細瞭解)

      另外還需要注意的是,不是所有版本的 MySQL 都默認開啓了服務器的二進制日誌,在進行主從同步的時候需要先檢查服務器是否已經開啓了二進制日誌。

      二進制日誌是一個文件,在進行網絡傳輸的過程中就一定會存在一些延遲,比如 200ms,這樣就可能造成用戶在從庫上讀取的數據不是最新的數據,也就會造成主從同步中的數據不一致的情況發生。比如對一條記錄進行更新,這個操作是在主庫上完成的,而在很短的時間內,比如 100ms,又對同一個記錄進行讀取,此時從庫還沒有完成數據的同步,那麼通過從庫讀取到的數據就是一條舊數據。這種情況下該怎麼辦呢?

04  如何解決主從同步的數據一致性問題

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

      可以想象下如果想要操作的數據都存儲在同一個數據庫中,那麼對數據進行更新的時候可以對記錄進行加寫鎖,這樣在讀取的時候就不會發生數據不一致的情況。但這時從庫的作用就是備份數據,沒有做到讀寫分離,分擔主庫的壓力。

      因此還需要想辦法在進行讀寫分離的時候,解決主從同步中數據不一致的問題,也就是解決主從之間數據複製方式的問題,如果按照數據一致性從弱到強來進行劃分,有以下三種複製方式。

4.1  全同步複製

      全同步複製是當主庫執行完一個事務之後,要求所有的從庫也都必須執行完該事務,纔可以返回處理結果給客戶端;因此,雖然全同步複製數據一致性得到保證了,但是主庫完成一個事物需要等待所有從庫也完成,性能就比較低了。如下圖:

圖 4 全同步複製示例圖

4.2  異步複製

       異步複製是當主庫提交事物後,會通知 binlog dump 線程發送 binlog 日誌給從庫,一旦 binlog dump 線程將 binlog 日誌發送給從庫之後,不需要等到從庫也同步完成事務,主庫就會將處理結果返回給客戶端。

       因爲主庫只需要執行完事務就可以將處理結果返回給客戶端,而不用關心從庫是否執行完事務,這就可能導致短暫的主從數據不一致的問題了,比如剛在主庫插入的新數據,如果馬上在從庫查詢就可能查詢不到。

      當主庫提交事物後,如果宕機掛掉了,此時可能 binlog 還沒來得及同步給從庫,這時候如果爲了恢復故障切換主從節點的話,就會出現數據丟失的問題,所以異步複製雖然性能高,但數據一致性上是最弱的。

mysql 主從複製,默認採用的就是異步複製這種複製策略。

圖 5 異步複製示例圖

4.3 半同步複製

     MySQL5.5 版本之後開始支持半同步複製的方式。原理是在客戶端提交 COMMIT 之後不直接將結果返回給客戶端,而是等待至少有一個從庫收到了 Binlog,並且寫入到中繼日誌中,再返回給客戶端。這樣做的好處是提高了數據一致性,相比於異步複製來說,至少多增加了一個網絡連接的延遲,降低了主庫寫的效率。

     在 MySQL5.7 版本中還增加了一個 rpl_semi_sync_master_wait_for_slave_count 參數,可以對需要響應的從庫數量進行設置,默認爲 1,也就是說只要有一個從庫進行了響應,就可以返回給客戶端。如果將這個參數調大,可以提升數據一致性的強度,但也會增加主庫等待從庫響應的時間。

圖 6 半同步複製示例圖

但是,半同步複製也存在以下幾個問題:

      當主庫成功提交事物並處於等待從庫確認的過程中,這個時候,從庫還沒來得及返回處理結果給客戶端,但因爲主庫存儲引擎內部已經提交事務了,所以,其他客戶端是可以從主庫中讀到數據的。

      但是,如果下一秒主庫突然宕機,此時正好下一次請求過來,就只能把請求切換到從庫中,因爲從庫還沒從主庫同步完畢數據,所以,從庫中就不會讀到這條數據,和上一秒讀取數據的結果對比就造成了幻讀的現象。

4.4  增強半同步複製

      增強半同步複製,是 mysql 5.7.2 後的版本對半同步複製做的一個改進,原理上幾乎是一樣的,主要是解決幻讀的問題。

      主庫配置了參數:rpl_semi_sync_master_wait_point = AFTER_SYNC 後,主庫在存儲引擎提交事務前,必須先收到從庫數據同步完成的確認信息後,才能提交事務,以此來解決幻讀問題。參考下圖:

圖 7 增強半同步複製示例圖

05  總結

理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

      通過上述內容可以瞭解 Mysql 數據庫的主從同步,但如果目標僅是數據庫的高併發,那麼可以先從 SQL 優化,索引以及 Redis 緩存數據等這些方面來考慮優化,其次再考慮是否採用主從架構的方式。

      在主從架構的配置中,如果想要採取讀寫分離的策略,既可以自己編寫程序,也可以通過第三方的中間件來實現。

      自己編寫程序的好處是比較自主,可以自行判斷哪些查詢在從庫上來執行,針對實時性要求高的需求,同時還可以考慮哪些查詢可以在主庫上執行;程序直接連接數據庫,減少了中間件層,可以減少一些性能損耗。

       而採用中間件的方法有很明顯的優勢:功能強大,使用簡單。但因爲在客戶端和數據庫之間增加了中間件層會有一些性能損耗,同時商業中間件價格較高,有一定學習成本。另外也可以考慮採用一些優秀的開源工具,比如 MaxScale。它是 MariaDB 開發的 MySQL 數據中間件,使用 MaxScale 作爲數據庫的代理,通過路由轉發完成了讀寫分離。同時也可以使用 MHA 工具作爲強一致的主從切換工具,從而完成 MySQL 的高可用架構。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/6uVsb70H37ME4HVnZMe8Ww