1. 複製概述
MySQL 內建的複製功能是構建基於 MySQL 的大規模、高效能應用的基礎,複製解決的基本問題是讓一臺伺服器的資料與其他伺服器保持同步。接下來,我們將從複製概述及原理、複製的配置、常見的問題及解決方法來學習 MySQL 的複製功能。
1.1 複製解決的問題
下面是複製常見的用途:
- 資料分佈。Mysql 複製通常不會對頻寬造成很大壓力,但在 5.1 版本中引入的基於行的複製會比傳統的基於語句的複製模式產生更大的頻寬壓力。你可以隨意地停止或開始複製,並在不同的地理位置來分佈資料備份,例如不同的資料中心。另外,即使在不穩定的網路環境下,遠端複製也可以工作。但如果未來儲存很低的複製延遲,最好有一個穩定、低延遲的連線。
- 負載均衡。通過 Mysql 複製,可以將讀操作分佈到多個伺服器上,實現對讀密集型應用的優化,並且很容易實現,通過簡單的程式碼修改就能實現基本的負載均衡。對應小規模的應用,可以簡單的使用 DNS 輪詢(將一個機器名指向多個 IP 地址)。
- 備份。對於備份來說,負載是一項很有意義的技術補充。
- 高可用性和故障切換。負載能夠幫助應用避免 Mysql 單點失敗,一個使用複製的設計良好的系統能夠顯著的縮短當機時間。
- Mysql 升級測試。這是比較常見的做法,在更新 Mysql 版本前,先使用將要更新的版本作為備庫,保證更新版本不會對系統造成影響。
1.2 複製是如何工作的?
在詳細介紹如何設定複製之前,讓我們先看看 Mysql 實際上是如何進行資料複製的。
總的來說,複製有三個步驟:
- 在主庫上把資料更改寫入到二進位制日誌(Binary Log)中(這些記錄被稱為二進位制日誌事件)。
- 備庫將主庫上的日誌複製到自己的中繼日誌(Relay Log)中。
- 備庫讀取中繼日誌中的事件,將其更改同步到備庫。
以上是複製的簡單概述,下圖描述了複製的細節:
整體複製過程:
- 在主庫上記錄二進位制日誌。在每次準備提交事務完成 資料更新前,主庫將資料更新的事件記錄到二進位制日誌中。Mysql 會按事務提交的順序而非每條語句的執行順序來記錄二進位制日誌。在記錄二進位制日誌後,主庫會告訴儲存引擎可以提交事務了。
- 備庫將主庫的二進位制日誌複製到其本地的中繼日誌中。首先,備庫會啟動一個工作執行緒,稱為 I/O 執行緒,I/O 執行緒跟主庫建立一個普通的客戶端連線,然後在主庫上啟動一個特殊的二進位制轉儲(binlog dump)執行緒,這個二進位制轉儲執行緒會讀取主庫二進位制日誌中的事件。它不會對時間進行輪詢。如果該執行緒“追趕”上了主庫,它將進入睡眠狀態,直到主庫傳送訊號量通知它有新的事件產生才會被換新,備庫 I/O 執行緒會將接收到的事件記錄到中繼日誌中。
- 備庫啟動 SQL 執行緒,執行最後一步。該執行緒從中繼日誌中讀取事件並在備庫執行,從而實現備庫資料的更新。當 SQL 執行緒追趕上 I/O 執行緒時,中繼日誌通常已經在系統快取中,所以中繼日誌的開銷很低。SQL 執行緒執行的事件也可以通過配置項來決定是否寫入自身的二進位制日誌中,這對於備庫再配置備庫的常見非常有用。
這種複製架構實現了獲取事件和重放事件的解耦,允許這兩個過程非同步進行。也就是說 I/O 執行緒能夠獨立於 SQL 執行緒之前工作。但是,這種架構也限制了複製的過程,其中最重要的一點是,在主庫上併發執行的查詢在備庫上只能序列化執行,因為只有一個 SQL 執行緒來重放中繼日誌中的事件。
不過值得高興的是,5.7 版本已經支援從庫的並行複製了。基於二進位制日誌的並行複製,是在日誌內容中新增了 last_committed 和 sequence_number,分別 表示事務提交的時間和上次事務提交的編號。如果事務具有相同的時間,表示這些事務是在一組內,可以進行並行回放。
2. 複製的原理
我們已經瞭解了複製的一些基本概念,接下來我們要更深入的瞭解複製,看看複製究竟是如何工作的,有哪些優缺點。
2.1 基於語句的複製
在 Mysql 5.0 及之前的版本中只支援基於語句的複製(也稱為邏輯複製)。基於語句的複製模式,主庫會記錄那些造成資料更改的 SQL 語句,當備庫讀取並重放這些事件時,實際上只是把主庫執行過的 SQL 再執行一遍。這種方式既有優點,也有缺點。
優點是:
- 實現簡單。理論上來說,只要簡單地記錄和執行 SQL 語句,就能夠讓主備保持同步。
- 二進位制日誌不會對頻寬產生較大影響。二進位制日誌裡的事件更加緊湊,佔用頻寬較小。
但事實上,基於語句的方式可能並不如其看起來那麼便利,其缺點是:
- 主庫上的資料除了執行的語句外,可能還依賴其他因素。當主庫使用 CURRENT_USER() 函式的語句,儲存過程和觸發器在使用基於語句的複製模式時就可能會出現問題。
2.2 基於行的複製
Mysql 5.1 開始支援基於行的複製。這種方式會將實際資料記錄在二進位制日誌中。同樣的,它也有其自身的優缺點。
它的優點是可以更加準確的複製資料,而缺點,則是可能造成較大的開銷。比如一個工資表中有一萬個使用者,我們把每個使用者的工資+1000,那麼基於行的複製則要複製一萬行的內容,由此造成的開銷比較大,而基於語句的複製僅僅一條語句就可以了。
由於沒有哪種模式是對所有情況都是完美的,Mysql 就使複製模式可以動態切換。預設情況下使用的是基於語句的複製方式,但如果發現語句無法被正確地複製,就切換到基於行的複製模式。還可以根據需要來設定會話級別的變數 binlog_format,控制二進位制日誌格式。
2.3 複製檔案解讀
複製過程中會使用到一些檔案。前面已經介紹了二進位制日誌檔案和中繼日誌檔案,除此之外,還有其他的檔案會被用到。
- mysql-bin.index:當在伺服器上開啟二進位制日誌時,同時會生成一個和二進位制日誌同名,但以 .index 作為字尾的檔案,該檔案用於記錄磁碟上的二進位制日誌檔案。這裡的 index 並不是表的索引,而是說這個檔案的每一行包含了二進位制檔案的檔名。Mysql 依賴這個檔案識別二進位制日誌檔案。
- mysql-relay-bin-index:中繼日誌的索引檔案,和 mysql-bin.index 的作用類似。
- master.info:儲存備庫連線主庫所需要的資訊檔案。格式為純文字(每行一個值),不同的 Mysql 版本,記錄的資訊也可能不太。此檔案不能刪除,否則備庫再重啟後不能連線主庫。這個檔案以文字的方式記錄了複製使用者的密碼,所以要注意此檔案的許可權控制。
- relay-log.info:記錄當前備庫複製的二進位制日誌和中繼日誌位置檔案。
使用這些檔案來記錄 Mysql 複製和日誌狀態是一種非常粗糙的方式。更不幸的是,它們不是同步寫的。如果伺服器斷電並且檔案資料沒有被重新整理到磁碟,在重啟伺服器後,檔案中記錄的資料可能是錯誤。不過好在這些問題以及在 5.5 版本里做了改進。
2.4 傳送複製事件到其它備庫
log_slave_update 選項可以讓備庫程式設計其它伺服器的主庫。在設定該選項後,Mysql 會將其執行過的事件記錄到它自己的二進位制日誌中。這樣它的備庫就可以從其日誌中檢索並執行事件。下圖闡述了這一過程:
在這種場景下,主庫將資料更新事件寫入二進位制日誌,第一個備庫提取並執行這個事件。這個時候一個事件的生命週期應該已經結束了。但由於設定了 log_slave_updates,備庫會將這個事件寫到它自己的二進位制日誌中。這樣第二個備庫就可以從第一個備庫中,將事件提取到它的中繼日誌中並執行。
這意味著作為源伺服器的主庫可以將其資料變化傳遞給沒有與其直接相連的備庫上。預設情況下,這個選項是被開啟的,這樣在連線到備庫時就不需要重啟伺服器。
當第一個備庫把自主庫獲得的事件寫入到其它二進位制日誌中時,這個事件在備庫二進位制日誌中的位置與其主庫二進位制日誌中的位置幾乎肯定是不相同的,可能在不同的日誌檔案或檔案內不同的位置。這意味著你不能假定所有擁有同一邏輯複製點的伺服器擁有相同的日誌座標。
小結
- 複製功能是 MySQL 高擴充套件性的基礎,常見的讀寫分離就使用了複製。
- 複製使用了三個執行緒。master 的日誌執行緒,將事件寫入 binlog,slave 的 IO 執行緒獲取 binlog,並將其寫入 relaylog,SQL 執行緒重放 relaylog 日誌。
- 複製有基於語句複製和基於行的複製。
參考:
- 高效能 MySQL - 第 3 版;