MySQL主從複製是其最重要的功能之一。主從複製是指一臺服務器充當主數據庫服務器,另外一臺或多臺服務器充當從數據庫服務器,主服務器中的數據自動複製到從服務器之中。對於多級複製,數據庫服務器便可充當主機,也可充當從機。MySQL主從複製的基礎是主服務器對數據庫修改記錄二進制日誌,從服務器經過主服務器的二進制日誌自動執行更新。html
主服務器上面執行的語句在從服務器上面再執行一遍,在MySQL-3.23版本之後支持。mysql
存在的問題:時間上可能不徹底同步形成誤差,執行語句的用戶也多是不一樣一個用戶。sql
把主服務器上面改編後的內容直接複製過去,而不關心到底改變該內容是由哪條語句引起的,在MySQL-5.0版本之後引入。shell
存在的問題:好比一個工資表中有一萬個用戶,咱們把每一個用戶的工資+1000,那麼基於行的複製則要複製一萬行的內容,由此形成的開銷比較大,而基於語句的複製僅僅一條語句就能夠了。數據庫
MySQL默認使用基於語句的複製,當基於語句的複製會引起問題的時候就會使用基於行的複製,MySQL會自動進行選擇。ubuntu
在MySQL主從複製架構中,讀操做能夠在全部的服務器上面進行,而寫操做只能在主服務器上面進行。主從複製架構雖然給讀操做提供了擴展,可若是寫操做也比較多的話(多臺從服務器還要從主服務器上面同步數據),單主模型的複製中主服務器勢必會成爲性能瓶頸。服務器
1、基於語句的複製:主服務器上面執行的語句在從服務器上面再執行一遍,在MySQL-3.23版本之後支持。網絡
存在的問題:時間上可能不徹底同步形成誤差,執行語句的用戶也多是不一樣一個用戶。session
2、基於行的複製:把主服務器上面改編後的內容直接複製過去,而不關心到底改變該內容是由哪條語句引起的,在MySQL-5.0版本之後引入。架構
存在的問題:好比一個工資表中有一萬個用戶,咱們把每一個用戶的工資+1000,那麼基於行的複製則要複製一萬行的內容,由此形成的開銷比較大,而基於語句的複製僅僅一條語句就能夠了。
3、混合類型的複製:MySQL默認使用基於語句的複製,當基於語句的複製會引起問題的時候就會使用基於行的複製,MySQL會自動進行選擇。
在MySQL主從複製架構中,讀操做能夠在全部的服務器上面進行,而寫操做只能在主服務器上面進行。主從複製架構雖然給讀操做提供了擴展,可若是寫操做也比較多的話(多臺從服務器還要從主服務器上面同步數據),單主模型的複製中主服務器勢必會成爲性能瓶頸。
三 MySQL主從複製工做原理
以下圖所示:
主服務器上面的任何修改都會保存在二進制日誌Binary log裏面,從服務器上面啓動一個I/O thread(實際上就是一個主服務器的客戶端進程),鏈接到主服務器上面請求讀取二進制日誌,而後把讀取到的二進制日誌寫到本地的一個Realy log裏面。從服務器上面開啓一個SQL thread定時檢查Realy log,若是發現有更改當即把更改的內容在本機上面執行一遍。
若是一主多從的話,這時主庫既要負責寫又要負責爲幾個從庫提供二進制日誌。此時能夠稍作調整,將二進制日誌只給某一從,這一從再開啓二進制日誌並將本身的二進制日誌再發給其它從。或者是乾脆這個從不記錄只負責將二進制日誌轉發給其它從,這樣架構起來性能可能要好得多,並且數據之間的延時應該也稍微要好一些。工做原理圖以下:
實際上在老版本的MySQL主從複製中Slave端並非兩個進程完成的,而是由一個進程完成。可是後來發現這樣作存在較大的風險和性能問題,主要以下:
首先,一個進程會使複製bin-log日誌和解析日誌並在自身執行的過程成爲一個串行的過程,性能受到了必定的限制,異步複製的延遲也會比較長。
另外,Slave端從Master端獲取bin-log過來以後,須要接着解析日誌內容,而後在自身執行。在這個過程當中,Master端可能又產生了大量變化並新增了大量的日誌。若是在這個階段Master端的存儲出現了沒法修復的錯誤,那麼在這個階段所產生的全部變動都將永遠沒法找回。若是在Slave端的壓力比較大的時候,這個過程的時間可能會比較長。
爲了提升複製的性能並解決存在的風險,後面版本的MySQL將Slave端的複製動做交由兩個進程來完成。提出這個改進方案的人是Yahoo!的一位工程師「Jeremy Zawodny」。這樣既解決了性能問題,又縮短了異步的延時時間,同時也減小了可能存在的數據丟失量。
固然,即便是換成了如今這樣兩個線程處理之後,一樣也仍是存在slave數據延時以及數據丟失的可能性的,畢竟這個複製是異步的。只要數據的更改不是在一個事物中,這些問題都是會存在的。若是要徹底避免這些問題,就只能用MySQL的cluster來解決了。不過MySQL的cluster是內存數據庫的解決方案,須要將全部數據都load到內存中,這樣就對內存的要求就很是大了,對於通常的應用來講可實施性不是太大。
還有一點要提的是MySQL的複製過濾(Replication Filters),複製過濾可讓你只複製服務器中的一部分數據。有兩種複製過濾:在Master上過濾二進制日誌中的事件;在Slave上過濾中繼日誌中的事件。以下:
配置Master的my.cnf文件(關鍵性的配置)/etc/my.cnf
log-bin=mysql-bin server-id = 1 binlog-do-db=icinga binlog-do-db=DB2 //若是備份多個數據庫,重複設置這個選項便可 binlog-do-db=DB3 //須要同步的數據庫,若是沒有本行,即表示同步全部的數據庫 binlog-ignore-db=mysql //被忽略的數據庫 配置Slave的my.cnf文件(關鍵性的配置)/etc/my.cnf log-bin=mysql-bin server-id=2 master-host=10.1.68.110 master-user=backup master-password=1234qwer master-port=3306 replicate-do-db=icinga replicate-do-db=DB2 replicate-do-db=DB3 //須要同步的數據庫,若是沒有本行,即表示同步全部的數據庫 replicate-ignore-db=mysql //被忽略的數據庫
網友說replicate-do-db的使用中可能會出些問題(http://blog.knowsky.com/19696...),本身沒有親自去測試。猜測binlog-do-db參數用於主服務器中,經過過濾Binary Log來過濾掉配置文件中不容許複製的數據庫,也就是不向Binary Log中寫入不容許複製數據的操做日誌;而replicate-do-db用於從服務器中,經過過濾Relay Log來過濾掉不容許複製的數據庫或表,也就是執行Relay Log中的動做時不執行那些不被容許的修改動做。這樣的話,多個從數據庫服務器的狀況:有的從服務器既從主服務器中複製數據,又作爲主服務器向另外的從服務器複製數據,那它的配置文件中應該能夠同時存在binlog-do-db、replicate-do-db這兩個參數纔對。一切都是本身的預測,關於binlog-do-db、replicate-do-db的具體使用方法還得在實際開發中一點點摸索才能夠。
網上有說,複製時忽略某些數據庫或者表的操做最好不要在主服務器上面進行,由於主服務器忽略以後就不會再往二進制文件中寫了,可是在從服務器上面雖然忽略了某些數據庫可是主服務器上面的這些操做信息依然會被複制到從服務器上面的relay log裏面,只是不會在從服務器上面執行而已。我想這個意思應該是建議在從服務器中設置replicate-do-db,而不要在主服務器上設置binlog-do-db。
另外,無論是黑名單(binlog-ignore-db、replicate-ignore-db)仍是白名單(binlog-do-db、replicate-do-db)只寫一個就好了,若是同時使用那麼只有白名單生效。
MySQL主從複製的兩種狀況:同步複製和異步複製,實際複製架構中大部分爲異步複製。
複製的基本過程以下:
Slave上面的IO進程鏈接上Master,並請求從指定日誌文件的指定位置(或者從最開始的日誌)以後的日誌內容。
Master接收到來自Slave的IO進程的請求後,負責複製的IO進程會根據請求信息讀取日誌指定位置以後的日誌信息,返回給Slave的IO進程。返回信息中除了日誌所包含的信息以外,還包括本次返回的信息已經到Master端的bin-log文件的名稱以及bin-log的位置。
Slave的IO進程接收到信息後,將接收到的日誌內容依次添加到Slave端的relay-log文件的最末端,並將讀取到的Master端的 bin-log的文件名和位置記錄到master-info文件中,以便在下一次讀取的時候可以清楚的告訴Master「我須要從某個bin-log的哪一個位置開始日後的日誌內容,請發給我」。
Slave的Sql進程檢測到relay-log中新增長了內容後,會立刻解析relay-log的內容成爲在Master端真實執行時候的那些可執行的內容,並在自身執行。
複製一般用來建立主節點的副本,經過添加冗餘節點來保證高可用性,固然複製也能夠用於其餘用途,例如在從節點上進行數據讀、分析等等。在橫向擴展的業務中,複製很容易實施,主要表如今在利用主節點進行寫操做,多個從節點進行讀操做,MySQL複製的異步性是指:事物首先在主節點上提交,而後複製給從節點並在從節點上應用,這樣意味着在同一個時間點主從上的數據可能不一致。異步複製的好處在於它比同步複製要快,若是對數據的一致性要求很高,仍是採用同步複製較好。
最簡單的複製模式就是一主一從的複製模式了,這樣一個簡單的架構只須要三個步驟便可完成:
(1)創建一個主節點,開啓binlog,設置服務器id;
(2)創建一個從節點,設置服務器id;
(3)將從節點鏈接到主節點上。
下面咱們開始操做,以MySQL 5.5爲例,操做系統Ubuntu12.10,Master 10.1.6.159 Slave 10.1.6.191。
apt-get install mysql-server
Master上面開啓binlog日誌,而且設置一個惟一的服務器id,在局域網內這個id必須惟一。二進制的binlog日誌記錄master上的全部數據庫改變,這個日誌會被複制到從節點上,而且在從節點上回放。修改my.cnf文件,在mysqld模塊下修改以下內容:
[mysqld] server-id = 1 log_bin = /var/log/mysql/mysql-bin.log
log_bin設置二進制日誌所產生文件的基本名稱,二進制日誌由一系列文件組成,log_bin的值是可選項,若是沒有爲log_bin設置值,則默認值是:主機名-bin。若是隨便修改主機名,則binlog日誌的名稱也會被改變的。server-id是用來惟一標識一個服務器的,每一個服務器的server-id都不同。這樣slave鏈接到master後,會請求master將全部的binlog傳遞給它,而後將這些binlog在slave上回放。爲了防止權限混亂,通常都是創建一個單獨用於複製的帳戶。
binlog是複製過程的關鍵,它記錄了數據庫的全部改變,一般即將執行完畢的語句會在binlog日誌的末尾寫入一條記錄,binlog只記錄改變數據庫的語句,對於不改變數據庫的語句則不進行記錄。這種狀況叫作基於語句的複製,前面提到過還有一種狀況是基於行的複製,兩種模式各有各的優缺點。
slave機器和master同樣,須要一個惟一的server-id。
[mysqld] server-id = 2
鏈接Slave到Master
在Master和Slave都配置好後,只須要把slave只想master便可
change master to master_host='10.1.6.159',master_port=3306,master_user='rep', master_password='123456'; start slave;
接下來在master上作一些針對改變數據庫的操做,來觀察slave的變化狀況。在修改完my.cnf配置重啓數據庫後,就開始記錄binlog了。能夠在/var/log/mysql目錄下看到一個mysql-bin.000001文件,並且還有一個mysql-bin.index文件,這個mysql-bin.index文件是什麼?這個文件保存了全部的binlog文件列表,可是咱們在配置文件中並無設置改值,這個能夠經過log_bin_index進行設置,若是沒有設置改值,則默認值和log_bin同樣。在master上執行show binlog events命令,能夠看到第一個binlog文件的內容。
注意:上面的sql語句是從頭開始複製第一個binlog,若是想從某個位置開始複製binlog,就須要在change master to時指定要開始的binlog文件名和語句在文件中的起點位置,參數以下:master_log_file和master_log_pos。
mysql> show binlog events\G *************************** 1. row *************************** Log_name: mysql-bin.000001 Pos: 4 Event_type: Format_desc Server_id: 1 End_log_pos: 107 Info: Server ver: 5.5.28-0ubuntu0.12.10.2-log, Binlog ver: 4 *************************** 2. row *************************** Log_name: mysql-bin.000001 Pos: 107 Event_type: Query Server_id: 1 End_log_pos: 181 Info: create user rep *************************** 3. row *************************** Log_name: mysql-bin.000001 Pos: 181 Event_type: Query Server_id: 1 End_log_pos: 316 Info: grant replication slave on *.* to rep identified by '123456' 3 rows in set (0.00 sec)
Log_name 是二進制日誌文件的名稱,一個事件不能橫跨兩個文件
Pos 這是該事件在文件中的開始位置
Event_type 事件的類型,事件類型是給slave傳遞信息的基本方法,每一個新的binlog都已Format_desc類型開始,以Rotate類型結束
Server_id 建立該事件的服務器id
End_log_pos 該事件的結束位置,也是下一個事件的開始位置,所以事件範圍爲Pos~End_log_pos-1
Info 事件信息的可讀文本,不一樣的事件有不一樣的信息
示例
在master的test庫中建立一個rep表,並插入一條記錄。
create table rep(name var); insert into rep values ("guol"); flush logs;
flush logs命令強制輪轉日誌,生成一個新的二進制日誌,能夠經過show binlog events in 'xxx'來查看該二進制日誌。能夠經過show master status查看當前正在寫入的binlog文件。這樣就會在slave上執行相應的改變操做。
上面就是最簡單的主從複製模式,不過有時候隨着時間的推動,binlog會變得很是龐大,若是新增長一臺slave,從頭開始複製master的binlog文件是很是耗時的,因此咱們能夠從一個指定的位置開始複製binlog日誌,能夠經過其餘方法把之前的binlog文件進行快速複製,例如copy物理文件。在change master to中有兩個參數能夠實現該功能,master_log_file和master_log_pos,經過這兩個參數指定binlog文件及其位置。咱們能夠從master上覆制也能夠從slave上覆制,假如咱們是從master上覆制,具體操做過程以下:
(1)爲了防止在操做過程當中數據更新,致使數據不一致,因此須要先刷新數據並鎖定數據庫:flush tables with read lock。
(2)檢查當前的binlog文件及其位置:show master status。
mysql> show master status\G *************************** 1. row *************************** File: mysql-bin.000003 Position: 107 Binlog_Do_DB: Binlog_Ignore_DB: 1 row in set (0.00 sec)
(3)經過mysqldump命令建立數據庫的邏輯備分:mysqldump --all-databases -hlocalhost -p >back.sql。
(4)有了master的邏輯備份後,對數據庫進行解鎖:unlock tables。
(5)把back.sql複製到新的slave上,執行:mysql -hlocalhost -p 把master的邏輯備份插入slave的數據庫中。
(6)如今能夠把新的slave鏈接到master上了,只須要在change master to中多設置兩個參數master_log_file='mysql-bin.000003'和master_log_pos='107'便可,而後啓動slave:start slave,這樣slave就能夠接着107的位置進行復制了。
change master to master_host='10.1.6.159',master_port=3306,master_user='rep', master_password='123456',master_log_file='mysql-bin.000003',master_log_pos='107'; start slave;
有時候master並不能讓你鎖住表進行復制,由於可能跑一些不間斷的服務,若是這時master已經有了一個slave,咱們則能夠經過這個slave進行再次擴展一個新的slave。原理同在master上進行復制差很少,關鍵在於找到binlog的位置,你在複製的同時可能該slave也在和master進行同步,操做以下:
(1)爲了防止數據變更,仍是須要中止slave的同步:stop slave。
(2)而後刷新表,並用mysqldump邏輯備份數據庫。
(3)使用show slave status查看slave的相關信息,記錄下兩個字段的值Relay_Master_Log_File和Exec_Master_Log_Pos,這個用來肯定從後面哪裏開始複製。
(4)對slave解鎖,把備份的邏輯數據庫導入新的slave的數據庫中,而後設置change master to,這一步和複製master同樣。
由一個master和一個slave組成複製系統是最簡單的狀況。Slave之間並不相互通訊,只能與master進行通訊。在實際應用場景中,MySQL複製90%以上都是一個Master複製到一個或者多個Slave的架構模式,主要用於讀壓力比較大的應用的數據庫端廉價擴展解決方案。
在上圖中,是咱們開始時提到的一主多從的狀況,這時主庫既要負責寫又要負責爲幾個從庫提供二進制日誌。這種狀況將二進制日誌只給某一從,這一從再開啓二進制日誌並將本身的二進制日誌再發給其它從,或者是乾脆這個從不記錄只負責將二進制日誌轉發給其它從,這樣架構起來性能可能要好得多,並且數據之間的延時應該也稍微要好一些。PS:這些前面都寫過了,又複製了一遍。
上圖中,Master-Master複製的兩臺服務器,既是master,又是另外一臺服務器的slave。這樣,任何一方所作的變動,都會經過複製應用到另一方的數據庫中。在這種複製架構中,各自上運行的不是同一db,好比左邊的是db1,右邊的是db2,db1的從在右邊反之db2的從在左邊,二者互爲主從,再輔助一些監控的服務還能夠實現必定程度上的高能夠用。
上圖中,這是由master-master結構變化而來的,它避免了M-M的缺點,實際上,這是一種具備容錯和高可用性的系統。它的不一樣點在於其中只有一個節點在提供讀寫服務,另一個節點時刻準備着,當主節點一旦故障立刻接替服務。好比經過corosync+pacemaker+drbd+MySQL就能夠提供這樣一組高可用服務,主備模式下再跟着slave服務器,也能夠實現讀寫分離。
這種結構的優勢就是提供了冗餘。在地理上分佈的複製結構,它不存在單一節點故障問題,並且還能夠將讀密集型的請求放到slave上。
早前的MySQL複製只能是基於異步來實現,從MySQL-5.5開始,支持半自動複製。在之前的異步(asynchronous)複製中,主庫在執行完一些事務後,是不會管備庫的進度的。若是備庫處於落後,而更不幸的是主庫此時又出現Crash(例如宕機),這時備庫中的數據就是不完整的。簡而言之,在主庫發生故障的時候,咱們沒法使用備庫來繼續提供數據一致的服務了。Semisynchronous Replication(半同步複製)則必定程度上保證提交的事務已經傳給了至少一個備庫。Semi synchronous中,僅僅保證事務的已經傳遞到備庫上,可是並不確保已經在備庫上執行完成了。
此外,還有一種狀況會致使主備數據不一致。在某個session中,主庫上提交一個事務後,會等待事務傳遞給至少一個備庫,若是在這個等待過程當中主庫Crash,那麼也可能備庫和主庫不一致,這是很致命的。若是主備網絡故障或者備庫掛了,主庫在事務提交後等待10秒(rpl_semi_sync_master_timeout的默認值)後,就會繼續。這時,主庫就會變回原來的異步狀態。
MySQL在加載並開啓Semi-sync插件後,每個事務需等待備庫接收日誌後才返回給客戶端。若是作的是小事務,兩臺主機的延遲又較小,則Semi-sync能夠實如今性能很小損失的狀況下的零數據丟失。