MySQL 複製 - 性能與擴展性的基石 4:主備切換

一旦使用 MySQL 的複製功能,就很大可能會碰到主備切換的狀況。也許是爲了迭代升級服務器,或者是主庫出現問題時,將一臺備庫轉換成主庫,或者只是但願從新分配容量。不過出於什麼緣由,都須要將新主庫的信息告訴其它備庫。mysql

對於主備切換,若是是計劃內的操做,較爲容易(至少比緊急狀況下容易)。只需在備庫簡單的使用 CHANGE MASTER TO 命令,並指定合適的值便可。並且大多數的值是可選的,只要指定須要改變的配置項接口。sql

備庫將拋棄以前的配置和中繼日誌,並重新的主庫開始複製。同時,新的參數會被更新到 master.info 文件中,這樣就算重啓,備庫配置信息也不會丟失。服務器

整個過程當中最難的是獲取新主庫上合適的二進制日誌位置。這樣備庫才能夠從老主庫相同的邏輯位置開始複製。工具

把備庫提高爲主庫要較爲麻煩,咱們把備庫提高主庫分爲計劃內切換計劃外切換兩種場景。spa

1 計劃內切換

備庫提高爲主庫,簡單來講,有如下步驟:日誌

  1. 中止向老主庫寫入。
  2. 讓備庫追遇上主庫(可選,能夠簡化後續的步驟)。
  3. 將一臺備庫配置爲新主庫。
  4. 將備庫和寫操做指向新主庫,而後開啓主庫寫入。

但上面的過程當中還所以着不少細節。一些場景可能依賴於複製的拓撲結構。更深刻一點,下面是大多數配置須要的步驟:blog

  1. 中止當前主庫上的全部寫操做。若是能夠,最好能將全部的客戶端程序關閉(除了複製鏈接)。
  2. 經過 FLUSH TABLE WITH READ LOCK 命令在主庫上中止全部活躍的寫入。也能夠在主庫上設置 read_only 選項。意味着從這一刻起,禁止向老主庫作任何寫入操做。由於一旦切換的新主庫,老主庫的寫入就意味着數據丟失。要注意的是,即便設置了 read_only 也不會阻止當前已存在的事務繼續提交。所以,能夠 kill 全部打開的事務,真正的結束全部寫入。
  3. 選擇一個備庫做爲新的主庫,並確保它已經徹底跟上主庫(例如,讓它執行完全部從主庫得到的中繼日誌)。
  4. 確保新主庫和老主庫數據一致。
  5. 在新主庫上執行 STOP SLAVE。
  6. 在新主庫上執行 CHANGE MASTER TO MASTER_HOST='',而後再執行 RESET SLAVE,使其斷開與老主庫的鏈接,並丟棄 master.info 裏記錄的信息(若是鏈接信息記錄在 my.cnf 裏,會沒法正常工做,所以咱們建議不要把複製鏈接信息寫到配置文件裏)。
  7. 執行 SHOW MASTER STATUS 記錄新主庫的二進制日誌座標。
  8. 確保其它備庫已經追遇上老主庫。
  9. 關閉老主庫。
  10. 將客戶端鏈接到新主庫。
  11. 在每臺備庫上執行 CHANGE MASTER TO 語句,使用以前得到的二進制日誌座標,指向新的主庫。

2 計劃外切換

當主庫崩潰時,須要將一臺備庫提高爲主庫。這個過程就比較麻煩。若是隻有一臺備庫,能夠直接使用這臺備庫。但若是有超過一臺的備庫,就須要作一些額外的工做。接口

另外,還有潛在的丟失複製事件的問題。可能有主庫上已發生的修改尚未更新到它任何一臺備庫上的狀況。甚至可能一條語句在主庫上執行了回滾,但在備庫上沒有回滾,這樣備庫可能就超過主庫的邏輯複製位置。若是能在某一點恢復主庫的數據,也許就能夠取得丟失語句,並手動執行他們。事件

在如下描述中,須要確保在服務器中使用 Master_Log_File 和 Read_Master_Log_Pos 的值。事務

2.1 主備結構之備庫提高

  1. 肯定哪臺備庫的數據最新。檢查每臺備庫上 SHOW_SLAVE_STATUS 命令的輸出,選擇其中 Master_Log_File 和 Read_Master_Log_Pos 的值最新的那個。
  2. 讓全部備庫執行完全部從老主庫崩潰前得到的中繼日誌。
  3. 在新主庫上執行 STOP SLAVE。
  4. 在新主庫上執行 CHANGE MASTER TO MASTER_HOST='',而後再執行 RESET SLAVE,使其斷開與老主庫的鏈接,並丟棄 master.info 裏記錄的信息。
  5. 執行 SHOW MASTER STATUS 記錄新主庫的二進制日誌座標。
  6. 比較每臺備庫和新主庫上的 Master_Log_File 和 Read_Master_Log_Pos 的值。
  7. 將客戶端鏈接到新主庫。
  8. 在每臺備庫上執行 CHANGE MASTER TO 語句,使用以前得到的二進制日誌座標,指向新的主庫。

若是已經在全部備庫上開啓了 log_bin 和 log_slave_updates,就能夠將全部備庫恢復到一個一致的時間點,若是沒有開啓這兩個選項,則很難作到這一點。

上面過程當中比較重要的一點是肯定日誌位置。接下來,咱們就來看看如何卻。

3 肯定日誌位置

若是有備庫和新主庫的位置不相同,則須要找到該備庫最後一條執行的事件在新主庫的二進制日誌中對應的位置,而後再執行 CHANGE MASTER TO。能夠經過 mysqlbinlog 工具來找到備庫執行的最後一條查詢,而後再主庫上找到一樣的查詢,進行簡單的計算便可獲得。

爲了便於描述,假設每一個日誌事件都有一個自增數字 ID。新主庫在老主庫崩潰時得到了編號爲 100 的事件,另外兩條備庫:R2 和 R3。R2 已結獲取了 99 號事件,R3 獲取了 98 號事件。

若是把 R2 和 R3 都指向新主庫的同一個二進制日誌位置,它們將從 101 號事件開始複製,從而致使數據不一樣步。但只要新主庫的二進制日誌已結經過 log_slave_updates 打開,就能夠在新主庫的二進制日誌中找到 99 號 和 100 號事件,從而將備庫恢復到一致的狀態。

因爲服務器重啓,不一樣的配置,日誌輪轉或者 FLUSH LOGS 命令,同一個事件在不一樣的服務器上可能有不一樣的偏移量。咱們能夠經過 mysqlbinlog 從二進制日誌或中繼日誌中解析出每臺備庫上執行的最後一個事件,並還有該命令解析新主庫上的二進制文件,找到相同的查詢,mysqlbinlog 會打印出該事件的偏移量,在 CHANGE MASTER TO 命令中使用這個值。

更快的方法是把新主庫和中止的備庫上的字節偏移量相減,它顯示了字節位置的差別。而後把這個值和新主庫當前二進制日誌的位置相減,就能夠獲得指望的查詢位置。

一塊兒來看個栗子。

假設 s1 是 s2 和 s3 的主庫。其中 s1 已經崩潰。根據 SHOW SLAVE STATUS 得到 Master_Log_File 和 Read_Master_Log_Pos 的值,s2 已結執行完了 s1 上全部的二進制日誌,但 s3 尚未。如圖 1:

圖 1:s1 崩潰,s2 已追遇上,s3 落後

咱們能夠確定 s2 已經執行完了主庫上的全部二進制日誌,由於 Master_log_File 和 Read_Master_Log_Pos 的值和 s1 上最後的日誌位置相吻合。所以,咱們能夠將 s2 提高爲新主庫,並將 s3 設置爲 s2 的備庫。

應該在 s3 上爲須要執行的 CHANGE MASTER TO 語句賦予什麼參數呢?這裏須要作一點計算。

s3 在偏移量 1493 處中止,比 s2 執行的最後一條語句的偏移量 1582 要小 89 字節。

s2 正在向偏移量爲 8167 的二進制日誌寫入,所以,理論上咱們應該將 s3 指向 s2 日誌的偏移量爲 8167-89=8078 的位置。

最後在 s2 日誌中的 8078 位置,肯定該位置上是不是正確的日誌事件。

若是驗證沒問題,能夠經過下面命令將 s3 切換爲 s2 的備庫:

CHANGE MASTER TO MASTER_HOST="s2 host", MASTER_LOG_FILE="mysql-bin.000009", MASTER_LOG_POS=8078;

若是服務器在它崩潰時已經執行完成並記錄了一個事件 a。由於 s2 僅僅讀取並執行到了 1582,所以可能會失去事件 a。可是若是老主庫的磁盤沒有損壞,仍然能夠經過 mysqlbinlog 或者從日誌服務器的二進制日誌中找到丟失的事件。

總結

  1. 備庫提高區分計劃內和計劃外場景。
  2. 備庫提高,找到新主庫準確的二進制日誌位置是關鍵。
相關文章
相關標籤/搜索