摘要
在這篇文章中,我將從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》中的圖來解釋:
這裡涉及到了有三個執行緒:
-
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:如果有其他的問題,也可以在公眾號找到我,歡迎來找我玩~