MySQL 複製 - 性能與擴展性的基石 3:常見問題及解決方案

主備複製過程當中有很大可能會出現各類問題,接下來咱們就討論一些比較廣泛的問題,以及當遇到這些問題時,如何解決或者預防問題發生。mysql

1 數據損壞或丟失

問題描述:服務器崩潰、斷電、磁盤損壞、內存或網絡錯誤等問題,致使數據損壞或丟失。 問題緣由:非正常關機致使沒有把數據及時的寫入硬盤。sql

這種問題,通常能夠分爲幾種狀況致使:數據庫

1.1 主庫意外關閉

問題未發生,避免方案:設置主庫的 sync_binlog 選項爲 1。此選項表示 MySQL 是否控制 binlog 的刷新。當設置爲 1 時,表示每次事務提交,MySQL 都會把 binlog 刷下去,是最安全,性能損耗也最大的設置問題已發生,解決方案:指定備庫從下一個二進制日誌的開頭從新讀日誌。可是一些日誌事件將永久性丟失。可使用 Percona Toolkit 中的 pt-table-checksum 工具來檢查主備一致性,以便於修復。安全

1.2 備庫意外關閉

備庫意外關閉重啓時,會去讀 master.info 文件以找到上次中止複製的位置。可是在乎外關閉的狀況下,這個文件存儲的信息多是錯誤的。此外,備庫也可能會嘗試從新執行一些二進制文件,這可能會致使惟一索引錯誤。咱們能夠經過 Percona Toolkit 中的 pt-slave-restart 工具,幫助備庫從新執行日誌文件。bash

若是使用的是 InnoDB 表,能夠在重啓後觀察 MySQL 的錯誤日誌。InnoDB 在恢復過程當中會打印出恢復點的二進制日誌座標,可使用這個值來決定備庫指向主庫的偏移量。服務器

1.3 主庫二進制日誌損壞

若是主庫上的二進制日誌損壞,除了忽略損壞的位置外,別無選擇。在忽略存貨位置後,咱們能夠經過 FLUSH LOGS 命令在主庫開始一個新的日誌文件,而後將備庫指向該文件的開始位置。網絡

1.4 備庫中繼日誌損壞

若是主庫上的日誌是無缺的,有兩種解決方案: 1) 手工處理。找到 master binlog 日誌的 pos 點,而後從新同步。工具

2) 自動處理。mysql5.5 考慮到 slave 宕機中繼日誌損壞這一問題,只要在 slave 的的配置文件 my.cnf 裏增長一個參數 relay_log_recovery=1 便可。性能

1.5 二進制日誌與 InnoDB 事務日誌不一樣步

因爲各類各樣的緣由,MySQL 的複製碰到服務器崩潰、斷電、磁盤損壞、內存或網絡錯誤時,很難恢復當時丟失的數據。幾乎都須要從某個點開始重啓複製。優化

2 未定義的服務器 ID

若是沒有再 my.cnf 裏定義服務器 ID,雖然能夠經過 CHANGE MASTER TO 來設置備庫,但在啓動複製時會遇到:

mysql> START SLAVE;
ERROR 1200 (HY000): The server us bit configured as slave; fix in config file or with CHANGE MASTER TO
複製代碼

這個報錯可能會讓人困惑。由於咱們可能已經經過 CHANGE MASTER TO 設置了備庫,而且經過 SHOW MASTER STATUS 也確認了,爲何還會有這樣的報錯呢?咱們經過 SELECT @@server_id 能夠得到一個值,要注意的是,這個值只是默認值,咱們必須爲備庫顯式地設置服務器 ID。也就是在 my.cnf 裏顯示的設置服務器 ID。

3 對未複製數據的依賴性

若是在主庫上有備庫上不存在的數據庫或數據表,複製就很容易中斷,反之亦然。 對於前者,假設在主庫上有一個 single_master 表,備庫沒有。在主庫上對此表進行操做後,備庫在嘗試回放這些操做時就會出現問題,致使複製中斷。

對於後者,假設備庫上有一個 single_slave 表,主庫沒有。在主庫上執行建立 single_slave 表的語句時,備庫在回放該建表語句時就會出現問題。

對於此問題,咱們能作的就是作好預防:

  1. 主備切換時,儘可能在切換後對比數據,查清楚是否有不一致的表或庫。
  2. 必定不要在備庫執行寫操做。

4 丟失的臨時表

臨時表和基於語句的複製方式不相容。若是備庫崩潰或者正常關閉,任何複製線程擁有的臨時表都會丟失。重啓備庫後,全部依賴於該臨時表的語句都會失敗。

複製時出現找不到臨時表的異常時,能夠作:

  1. 直接跳過錯誤,或者手動地建立一個名字和結構相同的表來代替消失的的臨時表。

臨時表的特性:

  1. 只對建立臨時表的鏈接可見。不會和其餘擁有相同名字的臨時表的鏈接起衝突;
  2. 隨着鏈接關閉而消失,無須顯式的移除它們。

4.1 更好使用臨時表的方式

保留一個專用的數據庫,在其中建立持久表,把它們做爲僞臨時表,以模擬臨時表特性。只須要經過 CONNETCTION_ID() 的返回值,給臨時表建立惟一的名字。

僞臨時表的優劣勢 優點:

  1. 更容易調試應用程序。能夠經過別的鏈接來查看應用正在維護的數據;

劣勢:

  1. 比臨時表多一些開銷。建立較慢僞臨時表會較慢,由於表的 .frm 文件須要刷新到磁盤。

5 InnoDB 加鎖讀致使主備數據不一致

使用共享鎖,串行化更新,保證備庫複製時數據一致。

某些狀況下,加鎖讀能夠防止混亂。假設有兩張表:tab1 沒有數據,tab2 只有一行數據,值爲 99。此時,有兩個事務更新數據。事務 1 將 tab2 的數據插入到 tab1,事務 2 更新 tab2。

兩個事務更新數據,使用共享鎖串行化更新

  1. 事務 1 使用獲取 tab2 數據時,加入共享鎖,並插入 tab1;
  2. 同時,事務 2 更新 tab2 數據時,因爲寫操做的排它鎖機制,沒法獲取 tab2 的鎖,等待;
  3. 事務 1 插入數據後,刪除共享鎖,提交事務,寫入 binlog(此時 tab1 和 tab2 的記錄值 都是 99);
  4. 事務 2 獲取到鎖,更新數據,提交事務,寫入 binlog(此時 tab1 的記錄值爲 99,tab2 的記錄值爲 100)。

上述過程當中,第二步很是重要。事務 2 嘗試去更新 tab2 表,這須要在更新的行上加排他鎖(寫鎖)。排他鎖與其餘鎖不相容,包括事務 1 在行記錄上加的共享鎖。所以事務 2 須要等待事務 1 完成。備庫在根據 binlog 進行復制時,會按一樣的順序先執行事務 1,再執行事務 2。主備數據一致。

一樣的過程,若是事務 1 在第一步時沒有加共享鎖,流程就變成:

兩個事務更新數據,未使用共享鎖串行化更新

  1. 事務 1 無鎖讀取 tab2 數據,並插入 tab1(此時 tab1 和 tab2 的記錄值 都是 99);
  2. 同時,事務 2 更新 tab2 數據,先與事務 1 提交事務,寫入 binlog(此時 tab1 的記錄值爲 99,tab2 的記錄值爲 100);
  3. 事務 1 提交事務,寫入 binlog(此時記錄值無變化);

mysqldump --single-transaction --all-databases --master-data=1 --host=server1 | mysql --host=server2 要注意的是,上述過程當中,事務 2 先提交,先寫入 binlog。在備庫複製時,一樣先執行事務 2,將 tab2 的記錄值更新爲 100。而後執行事務 1,讀取 tab2 數據,插入 tab1,因此最終的結果是,tab1 的記錄值和 tab2 的記錄值都是 100。很明顯,數據和主庫有差別。

建議在大多數狀況下將 innodb_unsafe_for_binlog 的值設置爲 0。基於行的複製因爲記錄了數據的變化而非語句,所以不會存在這個問題。

6 複製延遲過大

產生延遲的兩種方式

  1. 忽然產生延遲,而後再跟上;
  2. 穩定的延遲增大

前者一般是因爲一條執行時間過長的 SQL 致使,然後者即便在沒有慢語句也會出現。

對於前者,咱們能夠經過備庫上的慢查詢日誌來進行優化。在備庫上開啓 log_slow_slave_statement 選項,能夠在慢查詢日誌中記錄複製線程執行的語句。

而對於後者,沒有針對性的解決方案,只能經過各類方式提升備庫的複製效率。而當咱們想去對備庫作優化時,會發現,除了購買更快的磁盤和 CPU,並無太多的調優空間。只能經過 MySQL 選項禁止某些額外的工做以減小備庫的複製。能夠經過下面幾種方式:

  1. 使用 InnoDB 引擎時,設置 innodb_flush_log_at_trx_commit 值爲 2,來使備庫不要頻繁的刷新磁盤,以提升事務提交效率。
  2. 禁止二進制日誌記錄。把 innodb_locks_unsafe_for_binlog 設置爲 1,並把 MyISAM 的 delay_key_write 設置爲 ALL。要注意的是,這些設置是以安全換取速度,在將備庫提高爲主庫時,記得把這些選項設置回安全的值。
  3. 拆分效率較低的複製 SQL,分離複雜語句中的 SELECT 和 UPDATE 語句,下降複製消耗,提升效率。

總結

  1. 複製問題要分清楚是 master 的問題,仍是 slave 的問題。
  2. master 問題找 binlog,slave 問題找 relaylog。
相關文章
相關標籤/搜索