MySQL 入門(5):複製
阿新 • • 發佈:2020-05-18
## 摘要
在這篇文章中,我將從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