從MySQL 5.5到5.7看複製的演進
概要:MySQL 5.5 支援單執行緒模式複製,MySQL 5.6 支援庫級別的並行複製,MySQL 5.7 支援事務級別並行複製。結合這個主線我們可以來分析一下MySQL以及社群發展的一個前因後果。
MySQL5.5,對於複製我們可以這樣理解:主庫有個 dump binlog thread 不停的 dump binlog,然後以event為單位傳送給從庫 的 iothread,iothread 收到主庫傳過來的event寫入relaylog ,隨後sql_thread 讀取relaylog 對這些event以事務為單位進行回放。
那麼對於MySQL 5.5這個版本,在我們的使用過程中遇到那些問題,或者有那些不便呢?
-
首先DB壓力偏大時,從庫帶來的延遲較大,影響只讀業務
由於新硬體的發展,SSD的引入和多core的CPU,master節點的併發處理能力持續提升,slave節點完全按照binlog寫入順序的單執行緒回放,已完全跟不上master節點的吞吐能力。
在不考慮主從硬體配置差異情況下,延遲大的其根本原因在於:Master壓力過大,而Slave是單執行緒回放日誌。那麼要解決這個問題,從技術上來說可以把單執行緒變為多執行緒,利用並行帶來的優勢;從業務上來說可以進行拆庫,把一些業務線或者功能模組獨立出去;更進一步我們可以拆表,把壓力分擔到多個Master上去。
-
假如我們在不變動業務的情況下,從技術面來解決這個問題有哪些方向呢:
-
社群的解決方案:阿里開源的canal,基於表級別並行同步,可以減小同步延遲時間
-
官方的解決方案:在2011年10月份釋出了一個里程碑版本基於schema級別的並行複製[MySQL5.6.3 (multi-threaded slave)],以及基於group Commit的 MySQL5.7版本,最大化還原主庫並行度。
MySQL5.6,對於複製我們可以這樣理解,主庫有個 dump binlog thread 不停的 dump binlog,然後以event為單位傳送給從庫 的 iothread,io
MySQL5.7可以說是最大還原了主庫上的並行,在基於Group Commit的基礎上,所有在主庫上能夠完成prepared的語句表示沒有資料衝突,分配成相同的lastcommitted,就可以在slave節點並行複製。 那麼它是如何識別那些事務是一起提交的呢?其實就是在gtid event 中增加了兩個欄位【int64 lastcommitted;int64 sequencenumber】,當slave的coordinator執行緒在分發這些event的時候,具有相同lastcommitted 的事務(event的集合)就可以同時傳送給不同的work執行緒,達到並行同步的目的。
小結:就並行複製,按粒度區分有三種策略,粒度從粗到細是按庫、按表、按行。 這三個的對比中,並行度越來越大,額外損耗也是。無關大事務不會影響併發度。按照commit_id 的策略,適用範圍更廣,額外消耗也低。5.7的改進策略併發性更優。但出現大事務會拖後腿。
-
-
那麼我們只有一例項只有一個database,這種情況下我們就只有拆庫拆表了:
對於這種情況下,我們可以選擇在應用層做分庫分表,也可以選擇搞個中間層。不同的方案有不同的優劣。
-
應用層具有較好的效能,但是程式碼耦合在業務,如果後續擴容還需該程式碼,不能做到平滑擴容拆分,假如有多個業務都需要實現同樣的功能,那麼會帶來重複的工作量,而且工作難度也上升一個臺階。
-
中介軟體層具有較好的擴充套件性,低耦合性,如果DB擴容拆分,應用可以做到無感知,無改動。那麼也有一些成熟的開源方案,比如MyCAT,Cobar,Atlas,kingshard等。
-
-
-
其次主從切換時帶來的複雜度較大,需要計算position或者重做從庫
一般情況下我們的MySQL都是一主多從架構,這樣既能給我們提供讀寫分離、負載均衡的便利,也能給我們提供容災的能力。但是假如我們的主庫掛掉,這時我們會把從庫提升為主庫,但是在把從庫提升為新主的時候帶來了架構的微變化。為了還能利用以上便利、提供容災能力我們還得重新構建這個新主的多個從庫。此時問題就來了,我們從庫必須知道我當前應該從Master 的那個位置開始複製,也就是說必須拿到Master的position 。為了拿到這個位置我們有兩種辦法,一種簡單粗暴,重做Slave;另一種是通過一些列複雜計算、補回差異資料,算出當前資料和新主資料的差異點,從而得到新主庫position,導致HA切換和資料保護帶來巨大的挑戰。
- MMM架構(Master-Master replication manager for MySQL)
MMM是一套支援雙主故障切換和雙主日常管理的指令碼程式,可以再主庫故障時保證熱備切換為新主庫,並且自動的將從庫指向新主。但是這個架構本身不能保證資料的一致性。
-
MHA架構(Master High Availability)
MHA目前在MySQL高可用方面是一個相對成熟的解決方案,在自動進行故障切換的過程中,能最大程度上保證資料的一致性,以達到真正意義上的高可用。
那麼HMA是如何最大程度保證資料一致的呢?當主庫down掉時,MHA試圖從宕機的主伺服器上儲存二進位制日誌,最大程度的保證資料的不丟失,但這並不總是可行的。如果主庫傳送down機,日誌會出現不同程度的丟失,有個解決辦法就是設定半同步複製。MHA在把從提升為主的過程中,會進行一系列日誌對比,找到最接近主庫的從庫提升為新主庫,把從庫間差異化的資料拿出來進行應用等等。
-
GTID (Global Transaction ID)
在MySQL 5.6 以後官方引入了GTID,即在整個叢集內部,每個事務都有全域性唯一的一個標識,這樣一來,當我們主庫傳送down掉,或者MySQL架構有調整的時候,我們就不用很頭疼的去計算position;或者去配置略為複雜的MHA。我們只需要輕輕鬆鬆敲個CHANGE MASTER 命令帶上AUTO_POSITION就可以了,然後關於MASTER該從哪個binlog開始推送event給Slave這個完全由MySQL來幫我們計算。這個真是DBA們的福音啊。
簡單看看,為什麼這麼GTID這麼神奇吧。在MySQL內部幫我們記錄著 gtidpurged 和gtidexecuted 兩個集合。顧名思義,gtidexecuted 代表的時當前已經執行過的GTID的集合;一般情況下我們binlog不可能永久儲存,那麼gtidpurged代表的就是當前binlog已經沒有的GTID集合,它是gtidexecuted的子集。我們知道在事務是不能跨binlog存在的,意味著每個binlog都會有一個完整的事務集合,同樣每個binlog檔案的 header 部分,也都存放著這個binlog以前的 gtidexecuted 集合。我們的Slave 在應用Binlog的時候都會記錄自己當前已經執行過的最後一個事務GTID,那麼我們在切換主庫的時候,Slave就會把這個ID給帶上,然後Master端就會拿到這個GTID和自己當前的gtidexecuted、gtidpurged 集合進行對比,從而給到Slave一個合理的解釋。
- MMM架構(Master-Master replication manager for MySQL)
OK,到這裡MySQL從5.5的單執行緒複製,到5.6基於Schema級別的複製,再到5.7最大化還原主庫的並行就接近尾聲了。同時在這期間我們還給出了一些社群上、或者非技術上的解決方案。