1、複製進程
Mysql的複製(Replication)是一個非同步複製,從一個Mysql instace(稱之為Master)複製到另一個Mysql instance(稱之Slave)。實現整個複製操作主要由三個進程完成的,其中兩個進程在Slave(Sql進程和IO進程),另外一個進程在 Master(IO進程)上。
要實施複製,首先必須開啟Master端的binary log(bin-log)功能,否則無法實現。因為整個複製過程實際上就是Slave從Master端擷取該日誌然後再在自己身上完全順序的執行日誌中所記錄的各種操作。
複製的基本過程如下:
1)、Slave上面的IO進程串連上Master,並請求從指定記錄檔的指定位置(或者從最開始的日誌)之後的日誌內容;
2)、Master接收到來自Slave的IO進程的請求後,通過負責複製的IO進程根據請求資訊讀取制定日誌指定位置之後的日誌資訊,返回給Slave 的IO進程。返回資訊中除了日誌所包含的資訊之外,還包括本次返回的資訊已經到Master端的bin-log檔案的名稱以及bin-log的位置;
3)、Slave的IO進程接收到資訊後,將接收到的日誌內容依次添加到Slave端的relay-log檔案的最末端,並將讀取到的Master端的 bin-log的檔案名稱和位置記錄到master-info檔案中,以便在下一次讀取的時候能夠清楚的告訴Master“我需要從某個bin-log的哪 個位置開始往後的日誌內容,請發給我”;
4)、Slave的Sql進程檢測到relay-log中新增加了內容後,會馬上解析relay-log的內容成為在Master端真實執行時候的那些可執行檔內容,並在自身執行。
實際上在老版本的Mysql的複製實現在Slave端並不是兩個進程完成的,而是由一個進程完成。但是後來發現這樣做存在較大的風險和效能問題,主要如下:
首先,一個進程就使複製bin-log日誌和解析日誌並在自身執行的過程成為一個串列的過程,效能受到了一定的限制,非同步複製的延遲也會比較長。
另外,Slave端從Master端擷取bin-log過來之後,需要接著解析日誌內容,然後在自身執行。在這個過程中,Master端可能又產生了大量 變化並聲稱了大量的日誌。如果在這個階段Master端的儲存出現了無法修複的錯誤,那麼在這個階段所產生的所有變更都將永遠無法找回。如果在Slave 端的壓力比較大的時候,這個過程的時間可能會比較長。
所以,後面版本的Mysql為瞭解決這個風險並提高複製的效能,將Slave端的複製改為兩個進程來完成。提出這個改進方案的人是Yahoo!的一位工程師“Jeremy Zawodny”。這樣既解決了效能問題,又縮短了非同步延時時間,同時也減少了可能存在的資料丟失量。當然,即使是換成了現在這樣兩個線程處理以後,同樣也還是存在slave資料延時以及資料丟失的可能性的,畢竟這個複製是非同步。只要資料的更改不是在一個事物中,這些問題都是會存在的。如果要完全避免 這些問題,就只能用mysql的cluster來解決了。不過mysql的cluster是記憶體資料庫的解決方案,需要將所有資料都load到記憶體中,這樣就對記憶體的要求就非常大了,對於一般的應用來說可實施性不是太大。
2、複製實現層級
Mysql的複製可以是基於一條語句(Statement level),也可以是基於一條記錄(Row level),可以在Mysql的配置參數中設定這個複製層級,不同複製層級的設定會影響到Master端的bin-log記錄成不同的形式。
Row Level:日誌中會記錄成每一行資料被修改的形式,然後在slave端再對相同的資料進行修改。
優點:在row level模式下,bin-log中可以不記錄執行的sql語句的上下文相關的資訊,僅僅只需要記錄那一條記錄被修改了,修改成什麼樣了。所以row level的日誌內容會非常清楚的記錄下每一行資料修改的細節,非常容易理解。而且不會出現某些特定情況下的預存程序,或function,以及 trigger的調用和觸發無法被正確複製的問題。
缺點:row level下,所有的執行的語句當記錄到日誌中的時候,都將以每行記錄的修改來記錄,這樣可能會產生大量的日誌內容,比如有這樣一條update語 句:update product set owner_member_id = ‘b’ where owner_member_id = ‘a’,執行之後,日誌中記錄的不是這條update語句所對應額事件(mysql以事件的形式來記錄bin-log日誌),而是這條語句所更新的每一條 記錄的變化情況,這樣就記錄成很多條記錄被更新的很多個事件。自然,bin-log日誌的量就會很大。尤其是當執行alter table之類的語句的時候,產生的日誌量是驚人的。因為Mysql對於alter table之類的表結構變更語句的處理方式是整個表的每一條記錄都需要變動,實際上就是重建了整個表。那麼該表的每一條記錄都會被記錄到日誌中。
Statement Level:每一條會修改資料的sql都會記錄到 master的bin-log中。slave在複製的時候sql進程會解析成和原來master端執行過的相同的sql來再次執行。
優點:statement level下的優點首先就是解決了row level下的缺點,不需要記錄每一行資料的變化,減少bin-log日誌量,節約IO,提高效能。因為他只需要記錄在Master上所執行的語句的細節,以及執行語句時候的內容相關的資訊。
缺點:由於他是記錄的執行語句,所以,為了讓這些語句在slave端也能正確執行,那麼他還必須記錄每條語句在執行的時候的一些相關資訊,也就是上下文信 息,以保證所有語句在slave端杯執行的時候能夠得到和在master端執行時候相同的結果。另外就是,由於Mysql現在發展比較快,很多的新功能不斷的加入,使mysql得複製遇到了不小的挑戰,自然複製的時候涉及到越複雜的內容,bug也就越容易出現。在statement level下,目前已經發現的就有不少情況會造成mysql的複製出現問題,主要是修改資料的時候使用了某些特定的函數或者功能的時候會出現,比如:sleep()函數在有些版本中就不能真確複製,在預存程序中使用了last_insert_id()函數,可能會使slave和master上得到 不一致的id等等。由於row level是基於每一行來記錄的變化,所以不會出現類似的問題。
從官方文檔中看到,之前的Mysql一直都只有基於statement的複製模式,直到5.1.5版本的Mysql才開始支援row level的複製。從5.0開始,Mysql的複製已經解決了大量老版本中出現的無法正確複製的問題。但是由於預存程序的出現,給Mysql的複製又帶來了更大的新挑戰。另外,看到官方文檔說,從5.1.8版本開始,Mysql提供了除Statement Level和Row Level之外的第三種複製模式:Mixed,實際上就是前兩種模式的結合。在Mixed模式下,Mysql會根據執行的每一條具體的sql語句來區分對 待記錄的日誌形式,也就是在Statement和Row之間選擇一種。新版本中的Statment level還是和以前一樣,僅僅記錄執行的語句。而新版本的Mysql中隊row level模式也被做了最佳化,並不是所有的修改都會以row level來記錄,像遇到表結構變更的時候就會以statement模式來記錄,如果sql語句確實就是update或者delete等修改資料的語句,那麼還是會記錄所有行的變更。
3、複製常用架構
Mysql複製環境90%以上都是一個Master帶一個或者多個Slave的架構模式,主要用於讀壓力比較大的應用的資料庫端廉價擴充解決方案。因為只要master和slave的壓力不是太大(尤其是slave端壓力)的話,非同步複製的延時一般都很少很少。尤其是自slave端的複製方式改成兩個進程 處理之後,更是減小了slave端的延時。而帶來的效益是,對於資料即時性要求不是特別的敏感度的應用,只需要通過廉價的pc server來擴充slave的數量,將讀壓力分散到多台slave的機器上面,即可解決資料庫端的讀壓力瓶頸。這在很大程度上解決了目前很多中小型網站 的資料庫壓力瓶頸問題,甚至有些大型網站也在使用類似方案解決資料庫瓶頸。
一個Master帶多個slave的架構實施非常簡單,多個slave和單個slave的實施並沒有太大區別。在Master端並不care有多少個slave連上了master端,只要有slave進程通過了串連認證,向他請求binlog資訊,他就會按照串連上來的io進程的要求,讀取自己的 binlog資訊,返回給slave的IO進程。對於slave的配置細節,在Mysql的官方文檔上面已經說的很清楚了,甚至介紹了多種實現slave 的配置方法。
Mysql不支援一個Slave instance從屬於多個Master的架構。就是說,一個slave instance只能接受一個master的同步源,聽說有patch可以改進這樣的功能,但沒有實踐過。Mysql AB之所以不實現這樣的功能,主要是考慮到衝突解決的問題。
Mysql也可以搭建成dual master模式,也就是說兩個Mysql instance互為對方的Master,也同時為對方的Slave。不過一般這種架構也是只有一端提供服務,避免衝突問題。因為即使在兩邊執行的修改有 先後順序,由於複製的非同步實現機制,同樣會導致即使在晚做的修改也可能會被早做的修改所覆蓋,就像如下情形:
時間點 Mysql A Mysql B
1 更新x表y記錄為10
2 更新x表y記錄為20
3 擷取到A日誌並應用,更新x表的y記錄為10(不符合期望)
4 擷取B日誌更新x表y記錄為20(符合期望)
這樣,不僅在B庫上面的資料不是使用者所期望的結果,A和B兩邊的資料也出現了不一致的情況。除非能將寫操作根據某種條件固定分開在A和B兩端,保證不會交叉寫入,才能夠避免上面的問題。
原文連結:MySQL Replication(複製)基本原理