天天看點

MySQL Replication(複制)基本原理

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兩端,保證不會交叉寫入,才能夠避免上面的問題。

本文轉自 zouqingyun 51CTO部落格,原文連結:http://blog.51cto.com/zouqingyun/1674074,如需轉載請自行聯系原作者