1. 程式人生 > >MySQL 入門(5):複製

MySQL 入門(5):複製

## 摘要 在這篇文章中,我將從MySQL為什麼需要主從複製開始講起,然後會提到MySQL複製的前提,`bin log`。 在這裡會說明三種格式的`bin log`分別會有什麼優缺點。 隨後會講到主從延遲方面的問題,我將從幾個角度出發,提供一些可能造成延遲的思路。 ## 1 為什麼需要複製 MySQL內建的複製功能是構建大型,高效能應用程式的基礎。隨著目前併發量的增加,單機的MySQL漸漸沒有辦法承擔這些請求,所以MySQL伺服器也需要進行擴充套件。 MySQL的複製功能不僅可以提高可用性,還能用作災備,資料倉庫等。 ## 2 如何複製 說到複製,那麼問題的關鍵就在於資料從主庫複製到從庫時間需要多少,準確度能有多高。 對於MySQL來說,複製使用的是`bin log`。 對於`bin log`相信你不會陌生,我們在聊到MySQL的“兩階段提交”的時候有說到這個。 也就是說,MySQL會將主庫記錄的`bin log`傳送到從庫中,然後從庫按照`bin log`的內容,“重放一遍”主庫執行過的操作,達到主從同步的目的。 在這裡我們先詳細說一說`bin log`記錄了什麼。 ### 2.1 SBR(statement-based replication) 在這種模式下,`bin log`會完整的記錄下所執行的SQL語句。也就是說,如果使用了`statement`格式的`bin log`的話,主庫執行的SQL語句就會在從庫中完整的再執行一遍。 可是,這樣的做法,是有可能導致**主從不一致**的。 例如下面這樣的語句: ``` delete from t where a >= 1 and b => 2 limit 1; ``` 這樣的語句在從庫中就不一定能夠實現跟主庫一樣的效果。因為我們不能夠確定在從庫中是否走的跟主庫是同樣的索引,所查詢的第一條資料,是不是跟主庫一樣的,也就可能刪除的資料不是同一行。 又或者主庫執行的SQL語句裡面有一些鎖相關的語句,也可能會造成主從不一致的問題。 但是要注意的是,`NOW()`函式是可以被正確執行的,因為在`bin log`語句中會記錄時間戳。 也就是說,基於`statement`模式,在上下文不同的時候,是有可能造成資料不一致的。 至於其他的不安全情況,可以參考官方文件,這裡不展開介紹。 ### 2.2 RBR(row-based replication) 既然`statement`模式下會造成資料不一致,那麼有沒有一種模式是上下文無關的呢? 所以就有了`row`模式。 在這個模式中,`bin log`中只記錄了所操作的行的修改情況,會精確到某一行。 比如你更新了某一行,在`bin log`中就會記錄在id等於多少多少,某某欄位等於多少多少的行中,將某個欄位的值從A改成了B。 甚至是刪除操作,都會記錄刪除了id等於多少,A欄位等於多少,B欄位等於多少的一行資料。 聽到這裡你可能會覺得很方便,也很精確,主從不再會發生不一致的情況了。甚至於刪庫了都不需要跑路了,只需要檢視`bin log`就能恢復相應的資料了。 但是使用`row`模式同樣會有一些問題。比如你在主庫執行了`delete from t where id < 10000`這麼一行sql語句,如果使用`statement`格式,在`bin log`內記錄只有這麼一條,但是如果你使用的是`row`模式,那麼就需要記錄10000條資料,佔用很大的空間。 ### 2.3 MBR(mixd-based replication) 於是就有了`mixd`模式。 混合了以上兩種模式的優點,MySQL會在沒有歧義的時候使用`statement`格式,在有歧義的時候使用`row`格式。 ## 3 複製的具體過程 上面介紹了`bin log`的作用,以及`bin log`的組成形式,在這一章中我們聊一聊整個的複製流程。 我們拿《高效能MySQL》中的圖來解釋: ![](https://img2020.cnblogs.com/blog/1998080/202005/1998080-20200518082225139-1090144077.png) 這裡涉及到了有三個執行緒: * Binlog dump thread 這個執行緒在MySQL主庫中,負責讀取`bin log`中的內容,並將這些內容推送到從庫IO程序中。 * I/O thread 這個執行緒在MySQL從庫中,負責跟主庫建立一條長連線,並且將讀取到的`bin log`資料儲存到從庫的中繼日誌(relay log)中。 * SQL thread 這個執行緒也是在MySQL的從庫中,負責讀取中繼日誌中的內容,然後執行這些語句,將對資料的修改應用到從庫中。 簡單的來講,就是主庫經過兩階段提交後,把修改內容儲存在了`bin log`中,然後把這個`bin log`傳送給從庫,讓從庫也執行一次,以達到同步的目的。 而這裡採用了中繼日誌的原因是從庫消費`bin log`的速度和主庫生產`bin log`的速度是不一致的,所以需要一箇中繼日誌作為緩衝。 ## 4 複製可能造成的問題 在MySQL複製的過程中,經常出現的問題是**延遲**。 * 假設我們把主庫一個事務提交後`bin log`落盤的時間點設為t1 * 把從庫接受到主庫新事務寫的`bin log`並寫入`relay log`的時間節點設為t2 * 把從庫執行完這個新的事務的時間節點設為t3 那麼執行一條事務,從庫的延遲可以認為是(t3 - t1)。 也就是說,如果我們需要分析造成主從延遲的原因,應該從兩個方面考慮:傳輸過程,以及從庫消費`relay log`的速度。 ### 4.1 網路問題 網路確實可能會造成主從延遲,比如主庫或者從庫的頻寬打滿,又或者是主庫的`bin log`被設定成了`row`格式,導致有大量的資料需要傳輸,造成了主庫的`bin log`沒有及時的同步到從庫中,導致了主從的延遲。 ### 4.2 機器效能 但是除了網路,更多的是從庫的消費速度,跟不上主庫的生產速度。 這方面有很多原因,比如可能從庫的機器配置低於主庫,因為會有人覺得既然是備庫,沒什麼請求,就把備庫配置在了比較差的機器上面。 又或者在是後臺的資料分析,將CPU打滿。 總之,如果在從庫中需要有大量的查詢分析操作,需要考慮多個從庫。 ### 4.3 大事務 如果主庫執行了一條耗時很長的事務,那麼這條事務傳送到從庫中,可能也需要執行這麼長的時間。而這個時候,從庫是沒有辦法繼續消費新的`relay log`的。這就造成了主從延遲。 ### 4.4 鎖 我們之前提到過了,不僅僅寫資料會加鎖,使用“當前讀”,也一樣可能會加鎖。 所以,如果在從庫上執行了一些諸如`select ... for update`,或者一些DDL語句,可能也會造成從庫加鎖,導致主從延遲。 ### 4.5 併發 在我們上面的介紹中,SQL執行緒是單執行緒的,所以,如果能夠讓SQL執行緒可以併發消費,那麼主從延遲就可以大幅度的降低了。 關於MySQL的併發複製策略,MySQL5.6開始已經正式支援了,本文不詳細解釋。 ## 寫在最後 首先,謝謝你能看到這裡。 這一篇的文章,其實說的內容不多,大多都是一些理論性質的內容,目的是能夠對MySQL的主從複製有一些大體上的瞭解,並且知道對於延遲方面的問題,應該從哪個方向去考慮。 至於其他更具體的操作、如何調優,以及更深的原理,我想在今後的《進階篇》來提到。 並且,《MySQL 入門》系列到這裡就完結了。希望這五篇的內容能夠給你帶來一些幫助,能夠讓你對MySQL的瞭解更深一些。 當然了,在學習MySQL的過程中,我可能也會有一些錯誤的理解,如果有哪裡是不對的,希望你能指出,謝謝你! PS:如果有其他的問題,也可以在公眾號找到我,歡迎來找我玩~ ![](https://img2020.cnblogs.com/blog/1998080/202005/1998080-20200518082249440-18845376