雙機熱備的概念簡單說一下,就是要保持兩個數據庫的狀態自動同步。對任何一個數據庫的操作都自動應用到另外一個數據庫,始終保持兩個數據庫數據一致。 這樣做的好處多。 1. 可以做災備,其中一個壞了可以切換到另一個。 2. 可以做負載均衡,可以將請求分攤到其中任何一台上,提高網站吞吐量。 對於異地熱備,尤其適合災備。廢話不多說了。我們直接進入主題。 我們會主要介紹兩部分內容:
一, mysql 備份工作原理
二, 備份實戰
我們開始。
我使用的是mysql 5.5.34,
一, mysql 備份工作原理
簡單的說就是把 一個服務器上執行過的sql語句在別的服務器上也重復執行一遍, 這樣只要兩個數據庫的初態是一樣的,那么它們就能一直同步。
當然這種復制和重復都是mysql自動實現的,我們只需要配置即可。
我們進一步詳細介紹原理的細節, 這有一張圖:

上圖中有兩個服務器, 演示了從一個主服務器(master) 把數據同步到從服務器(slave)的過程。
這是一個主-從復制的例子。 主-主互相復制只是把上面的例子反過來再做一遍。 所以我們以這個例子介紹原理。
對於一個mysql服務器, 一般有兩個線程來負責復制和被復制。當開啟復制之后。
1. 作為主服務器Master, 會把自己的每一次改動都記錄到 二進制日志 Binarylog 中。 (從服務器會負責來讀取這個log, 然后在自己那里再執行一遍。)
2. 作為從服務器Slave, 會用master上的賬號登陸到 master上, 讀取master的Binarylog, 寫入到自己的中繼日志 Relaylog, 然后自己的sql線程會負責讀取這個中繼日志,並執行一遍。 到這里主服務器上的更改就同步到從服務器上了。
在mysql上可以查看當前服務器的主,從狀態。 其實就是當前服務器的 Binary(作為主服務器角色)狀態和位置。 以及其RelayLog(作為從服務器)的復制進度。
例如我們在主服務器上查看主狀態:
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000014
Position: 107
Binlog_Do_DB:
Binlog_Ignore_DB: mysql,information_schema,performance_schema,amh
1 row in set (0.00 sec)
稍微解釋一下這幾行的意思:
1. 第一行表明 當前正在記錄的 binarylog文件名是: mysql-bin.000014.
我們可以在mysql數據目錄下,找到這個文件:
2. 第二行, 107. 表示當前的文件偏移量, 就是寫入在mysql-bin.000014 文件的記錄位置。
這兩點就構成了 主服務器的狀態。 配置從服務器的時候,需要用到這兩個值。 告訴從服務器從哪讀取主服務器的數據。 (從服務器會登錄之后,找到這個日志文件,並從這個偏移量之后開始復制。)
3. 第三行,和第四行,表示需要記錄的數據庫和需要忽略的數據庫。 只有需要記錄的數據庫,其變化才會被寫入到mysql-bin.000014日志文件中。 后面會再次介紹這兩個參數。
我們還可以在從服務器上,查看從服務器的復制狀態。
1: mysql> show slave status\G
2: *************************** 1. row ***************************
3: Slave_IO_State: Waiting for master to send event
4: Master_Host: 198.**.***.***
5: Master_User: r*******
6: Master_Port: 3306
7: Connect_Retry: 60
8: Master_Log_File: mysql-bin.000014
9: Read_Master_Log_Pos: 107
10: Relay_Log_File: mysqld-relay-bin.000013
11: Relay_Log_Pos: 253
12: Relay_Master_Log_File: mysql-bin.000014
13: Slave_IO_Running: Yes
14: Slave_SQL_Running: Yes
15: Replicate_Do_DB:
16: Replicate_Ignore_DB: mysql,information_schema,amh,performance_schema
17: Replicate_Do_Table:
18: Replicate_Ignore_Table:
19: Replicate_Wild_Do_Table:
20: Replicate_Wild_Ignore_Table:
21: Last_Errno: 0
22: Last_Error:
23: Skip_Counter: 0
24: Exec_Master_Log_Pos: 107
25: Relay_Log_Space: 556
26: Until_Condition: None
27: Until_Log_File:
28: Until_Log_Pos: 0
29: Master_SSL_Allowed: No
我們還是來重點解釋途中的紅圈的部分:
1. Master_host 指的是 主服務器的地址。
2. Master_user 指的是主服務器上用來復制的用戶。 從服務器會用此賬號來登錄主服務。進行復制。
3. Master_log_file 就是前面提到的, 主服務器上的日志文件名.
4. Read_Master_log_pos 就是前面提到的主服務器的日志記錄位置, 從服務器根據這兩個條件來選擇復制的文件和位置。
5. Slave_IO_Running: 指的就是從服務器上負責讀取主服務器的線程工作狀態。 從服務器用這個專門的線程鏈接到主服務器上,並把日志拷貝回來。
6. Slave_SQL_Running: 指的就是專門執行sql的線程。 它負責把復制回來的Relaylog執行到自己的數據庫中。 這兩個參數必須都為Yes 才表明復制在正常工作。
其他的參數之后再介紹。
二, mysql 雙機熱備實戰
了解了上面的原理之后, 我們來實戰。 這里有兩個重點, 要想同步數據庫狀態, 需要相同的初態,然后配置同步才有意義。 當然你可以不要初態,這是你的自由。 我們這里從頭開始配置一遍。
我們先以A服務器為起點, 配置它的數據庫同步到B。 這就是主-從復制了。 之后再反過來做一次,就可以互相備份了。
1, 第一步,
在A上面創建專門用於備份的 用戶:
grant replication slave on *.* to 'repl_user'@'192.***.***.***' identified by 'hj34$%&mnkb';
上面把ip地址換成B機器的ip地址。 只允許B登錄。安全。
用戶名為: repl_user
密碼為: hj34$********nkb
這個等會在B上面要用。
2. 開啟主服務器的 binarylog。
很多服務器是默認開啟的,我們這里檢查一下:
打開 /etc/my.cnf
我來解釋一下紅框中的配置:
前面三行, 你可能已經有了。
binlog-do-db 用來表示,只把哪些數據庫的改動記錄到binary日志中。 可以寫上關注hello數據庫。 但是我把它注釋掉了。 只是展示一下。 可以寫多行,表示關注多個數據庫。
binlog-ignore-db 表示,需要忽略哪些數據庫。我這里忽略了其他的4個數據庫。
后面兩個用於在 雙主(多主循環)互相備份。 因為每台數據庫服務器都可能在同一個表中插入數據,如果表有一個自動增長的主鍵,那么就會在多服務器上出現主鍵沖突。 解決這個問題的辦法就是讓每個數據庫的自增主鍵不連續。 上圖說是, 我假設需要將來可能需要10台服務器做備份, 所以auto-increment-increment 設為10. 而 auto-increment-offset=1 表示這台服務器的序號。 從1開始, 不超過auto-increment-increment。
這樣做之后, 我在這台服務器上插入的第一個id就是 1, 第二行的id就是 11了, 而不是2.
(同理,在第二台服務器上插入的第一個id就是2, 第二行就是12, 這個后面再介紹) 這樣就不會出現主鍵沖突了。 后面我們會演示這個id的效果。
3. 獲取主服務器狀態, 和同步初態。
假設我現在有這些數據庫在A上面。
如果你是全新安裝的, 那么不需要同步初態,直接跳過這一步,到后面直接查看主服務器狀態。
這里我們假設有一個 hello 數據庫作為初態。
先鎖定 hello數據庫:
FLUSH TABLES WITH READ LOCK;
然后導出數據:
我這里只需要導出hello數據庫, 如果你有多個數據庫作為初態的話, 需要導出所有這些數據庫:
然后查看A服務器的binary日志位置:
記住這個文件名和 位置, 等會在從服務器上會用到。
主服務器已經做完了, 可以解除鎖定了:
4. 設置從服務器 B 需要復制的數據庫
打開從服務器 B 的 /etc/my.cnf 文件:
解釋一下上面的內容。
server-id 必須保證每個服務器不一樣。 這可能和循環同步有關。 防止進入死循環。
replicate-do-db 可以指定需要復制的數據庫, 我這里注掉了。 演示一下。
replicate-ignore-db 復制時需要排除的數據庫, 我使用了,這個。 除開系統的幾個數據庫之外,所有的數據庫都復制。
relay_log 中繼日志的名字。 前面說到了, 復制線程需要先把遠程的變化拷貝到這個中繼日志中, 在執行。
log-slave-updates 意思是,中繼日志執行之后,這些變化是否需要計入自己的binarylog。 當你的B服務器需要作為另外一個服務器的主服務器的時候需要打開。 就是雙主互相備份,或者多主循環備份。 我們這里需要, 所以打開。
保存, 重啟mysql。
5. 導入初態, 開始同步。
把剛才從A服務器上導出的 hello.sql 導入到 B的hello數據庫中, 如果B現在沒有hello數據庫,請先創建一個, 然后再導入:
創建數據庫:
mysql> create database hello default charset utf8;
把hello.sql 上傳到B上, 然后導入:
如果你剛才導出了多個數據庫, 需要把他們都一一上傳導入。
開啟同步, 在B服務器上執行:
CHANGE MASTER TO
MASTER_HOST='192.***.***.***',
MASTER_USER='repl_user', MASTER_PASSWORD='hj3****', MASTER_LOG_FILE='mysql-bin.000004', MASTER_LOG_POS=7145;
上面幾個參數我就不解釋了。 前面說過了。
重啟mysql, 然后查看slave線程開啟了沒:
注意圖中的紅框, 兩個都是Yes, 說明開啟成功。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
如果其中一個是No, 那就說明不成功。需要查看mysql的錯誤日志。 我在第一次做的時候就遇到這個問題。有時候密碼填錯了, 有時候防火牆的3306沒有打開。ip地址不對,等等。 都會導致失敗。
我們看錯誤日志: mysql的錯誤日志一般在:
文件名應該是你的機器名, 我這里叫做host1.err 你換成你自己的。
到這里主-從復制已經打開了。 我們先來實驗一下。
我們在A的數據庫里面去 添加數據:
我在A的 hello數據庫的test表中 連續插入了3條數據, 注意看他們的自增長id, 分別是1,11,21. 知道這是為什么嗎。 前面已經說過了,不懂再回去看。
我們去看一下B數據庫有沒有這三條數據:
打開B的數據庫:
發現已經在這了。 這里效果不直觀。
此時不要在B中修改數據。 我們接着配置從B到A的復制。 如果你只需要主從復制的話, 到這里就結束了。后面可以不看了。 所有A中的修改都能自動同步到B, 但是對B的修改卻不能同步到A。 因為是單向的。 如果需要雙向同步的話,需要再做一次從B到A的復制。
基本跟上面一樣:我們簡單一點介紹:
2. 打開 /etc/my.cnf , 開啟B的binarylog:
注意紅框中所新添加的部分。
3. 我們不需要導出B的初態了,因為它剛剛才從A導過來。 直接記住它的master日志狀態:
記住這兩個數值,等會在A上面要用。
B服務器就設置完了。
4. 登錄到A 服務器。 開啟中繼:
注意框中心添加的部分, 不解釋了。
5. 啟動同步:
上面的ip地址是B的ip地址, 因為A把B當做master了。 不解釋了。
然后重啟mysql服務。
然后查看,slave狀態是否正常:
圖中出現了兩個No。
Slave_IO_Running: No
Slave_SQL_Running: No
說明slave沒有成功, 即,從B到A的同步沒有成功。 我們去查看mysql錯誤日志,前面說過位置:
找到 機器名.err 文件,打開看看:
看圖中的error信息。 說找不到中繼日志文件。
這是因為我們在配置A的中繼文件時改了中繼文件名,但是mysql沒有同步。解決辦法很簡單。
先停掉mysql服務。 找到這三個文件,把他們刪掉。 一定要先停掉mysql服務。不然還是不成功。你需要重啟一下機器了。 或者手動kill mysqld。
好了, 啟動mysql之后。 我們在來檢查一下slave狀態:
注意圖中兩個大大的Yes。
1 復制概述
Mysql內建的復制功能是構建大型,高性能應用程序的基礎。將Mysql的數據分布到多個系統上去,這種分布的機制,是通過將Mysql的某一台主機的數據復制到其它主機(slaves)上,並重新執行一遍來實現的。復制過程中一個服務器充當主服務器,而一個或多個其它服務器充當從服務器。主服務器將更新寫入二進制日志文件,並維護文件的一個索引以跟蹤日志循環。這些日志可以記錄發送到從服務器的更新。當一個從服務器連接主服務器時,它通知主服務器從服務器在日志中讀取的最后一次成功更新的位置。從服務器接收從那時起發生的任何更新,然后封鎖並等待主服務器通知新的更新。
請注意當你進行復制時,所有對復制中的表的更新必須在主服務器上進行。否則,你必須要小心,以避免用戶對主服務器上的表進行的更新與對從服務器上的表所進行的更新之間的沖突。
1.1 mysql支持的復制類型:
(1):基於語句的復制: 在主服務器上執行的SQL語句,在從服務器上執行同樣的語句。MySQL默認采用基於語句的復制,效率比較高。
一旦發現沒法精確復制時, 會自動選着基於行的復制。
(2):基於行的復制:把改變的內容復制過去,而不是把命令在從服務器上執行一遍. 從mysql5.0開始支持
(3):混合類型的復制: 默認采用基於語句的復制,一旦發現基於語句的無法精確的復制時,就會采用基於行的復制。
1.2 . 復制解決的問題
MySQL復制技術有以下一些特點:
(1) 數據分布 (Data distribution )
(2) 負載平衡(load balancing)
(3) 備份(Backups)
(4) 高可用性和容錯行 High availability and failover
1.3 復制如何工作
整體上來說,復制有3個步驟:
(1) master將改變記錄到二進制日志(binary log)中(這些記錄叫做二進制日志事件,binary log events);
(2) slave將master的binary log events拷貝到它的中繼日志(relay log);
(3) slave重做中繼日志中的事件,將改變反映它自己的數據。
下圖描述了復制的過程:

該過程的第一部分就是master記錄二進制日志。在每個事務更新數據完成之前,master在二日志記錄這些改變。MySQL將事務串行的寫入二進制日志,即使事務中的語句都是交叉執行的。在事件寫入二進制日志完成后,master通知存儲引擎提交事務。
下一步就是slave將master的binary log拷貝到它自己的中繼日志。首先,slave開始一個工作線程——I/O線程。I/O線程在master上打開一個普通的連接,然后開始binlog dump process。Binlog dump process從master的二進制日志中讀取事件,如果已經跟上master,它會睡眠並等待master產生新的事件。I/O線程將這些事件寫入中繼日志。
SQL slave thread(SQL從線程)處理該過程的最后一步。SQL線程從中繼日志讀取事件,並重放其中的事件而更新slave的數據,使其與master中的數據一致。只要該線程與I/O線程保持一致,中繼日志通常會位於OS的緩存中,所以中繼日志的開銷很小。
此外,在master中也有一個工作線程:和其它MySQL的連接一樣,slave在master中打開一個連接也會使得master開始一個線程。復制過程有一個很重要的限制——復制在slave上是串行化的,也就是說master上的並行更新操作不能在slave上並行操作。
2 .復制配置
有兩台MySQL數據庫服務器Master和slave,Master為主服務器,slave為從服務器,初始狀態時,Master和slave中的數據信息相同,當Master中的數據發生變化時,slave也跟着發生相應的變化,使得master和slave的數據信息同步,達到備份的目的。
要點:
負責在主、從服務器傳輸各種修改動作的媒介是主服務器的二進制變更日志,這個日志記載着需要傳輸給從服務器的各種修改動作。因此,主服務器必須激活二進制日志功能。從服務器必須具備足以讓它連接主服務器並請求主服務器把二進制變更日志傳輸給它的權限。
環境:
Master和slave的MySQL數據庫版本同為5.0.18
操作系統:unbuntu 11.10
IP地址:10.100.0.100
2.1、創建復制帳號
1、在Master的數據庫中建立一個備份帳戶:每個slave使用標准的MySQL用戶名和密碼連接master。進行復制操作的用戶會授予REPLICATION SLAVE權限。用戶名的密碼都會存儲在文本文件master.info中
命令如下:
mysql > GRANT REPLICATION SLAVE,RELOAD,SUPER ON *.*
TO backup@’10.100.0.200’
IDENTIFIED BY ‘1234’;
建立一個帳戶backup,並且只能允許從10.100.0.200這個地址上來登陸,密碼是1234。
(如果因為mysql版本新舊密碼算法不同,可以設置:set password for 'backup'@'10.100.0.200'=old_password('1234'))
2.2、拷貝數據
(假如是你完全新安裝mysql主從服務器,這個一步就不需要。因為新安裝的master和slave有相同的數據)
關停Master服務器,將Master中的數據拷貝到B服務器中,使得Master和slave中的數據同步,並且確保在全部設置操作結束前,禁止在Master和slave服務器中進行寫操作,使得兩數據庫中的數據一定要相同!
2.3、配置master
接下來對master進行配置,包括打開二進制日志,指定唯一的servr ID。例如,在配置文件加入如下值:
server-id=1
log-bin=mysql-bin
server-id:為主服務器A的ID值
log-bin:二進制變更日值
重啟master,運行SHOW MASTER STATUS,輸出如下:
2.4、配置slave
log_bin = mysql-bin
server_id = 2
relay_log = mysql-relay-bin
log_slave_updates = 1
read_only = 1
server_id是必須的,而且唯一。slave沒有必要開啟二進制日志,但是在一些情況下,必須設置,例如,如果slave為其它slave的master,必須設置bin_log。在這里,我們開啟了二進制日志,而且顯示的命名(默認名稱為hostname,但是,如果hostname改變則會出現問題)。
relay_log配置中繼日志,log_slave_updates表示slave將復制事件寫進自己的二進制日志(后面會看到它的用處)。
有些人開啟了slave的二進制日志,卻沒有設置log_slave_updates,然后查看slave的數據是否改變,這是一種錯誤的配置。所以,盡量使用read_only,它防止改變數據(除了特殊的線程)。但是,read_only並是很實用,特別是那些需要在slave上創建表的應用。
2.5、啟動slave
接下來就是讓slave連接master,並開始重做master二進制日志中的事件。你不應該用配置文件進行該操作,而應該使用CHANGE MASTER TO語句,該語句可以完全取代對配置文件的修改,而且它可以為slave指定不同的master,而不需要停止服務器。如下:mysql> CHANGE MASTER TO MASTER_HOST='server1',
-> MASTER_USER='repl',
-> MASTER_PASSWORD='p4ssword',
-> MASTER_LOG_FILE='mysql-bin.000001',
-> MASTER_LOG_POS=0;
MASTER_LOG_POS的值為0,因為它是日志的開始位置。
你可以用SHOW SLAVE STATUS語句查看slave的設置是否正確:
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: server1
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: mysql-relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
...omitted...
Seconds_Behind_Master: NULL
Slave_IO_State, Slave_IO_Running, 和Slave_SQL_Running是No
表明slave還沒有開始復制過程。日志的位置為4而不是0,這是因為0只是日志文件的開始位置,並不是日志位置。實際上,MySQL知道的第一個事件的位置是4。
為了開始復制,你可以運行:
mysql> START SLAVE;
運行SHOW SLAVE STATUS查看輸出結果:
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: server1
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 164
Relay_Log_File: mysql-relay-bin.000001
Relay_Log_Pos: 164
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...omitted...
Seconds_Behind_Master: 0
Slave_IO_Running=Yes
Slave_SQL_Running=Yes
slave的I/O和SQL線程都已經開始運行,而且Seconds_Behind_Master不再是NULL。日志的位置增加了,意味着一些事件被獲取並執行了。如果你在master上進行修改,你可以在slave上看到各種日志文件的位置的變化,同樣,你也可以看到數據庫中數據的變化。
你可查看master和slave上線程的狀態。在master上,你可以看到slave的I/O線程創建的連接:
| mysql> show processlist \G *************************** 1. row *************************** Id: 1 User: root Host: localhost:2096 db: test Command: Query Time: 0 State: NULL Info: show processlist *************************** 2. row *************************** Id: 2 User: repl Host: localhost:2144 db: NULL Command: Binlog Dump Time: 1838 State: Has sent all binlog to slave; waiting for binlog to be updated Info: NULL 2 rows in set (0.00 sec) |
行2為處理slave的I/O線程的連接。
在slave服務器上運行該語句:
| mysql> show processlist \G *************************** 1. row *************************** Id: 1 User: system user Host: db: NULL Command: Connect Time: 2291 State: Waiting for master to send event Info: NULL *************************** 2. row *************************** Id: 2 User: system user Host: db: NULL Command: Connect Time: 1852 State: Has read all relay log; waiting for the slave I/O thread to update it Info: NULL *************************** 3. row *************************** Id: 5 User: root Host: localhost:2152 db: test Command: Query Time: 0 State: NULL Info: show processlist 3 rows in set (0.00 sec) |
行1為I/O線程狀態,行2為SQL線程狀態。
2.5、添加新slave服務器
假如master已經運行很久了,想對新安裝的slave進行數據同步,甚至它沒有master的數據。
此時,有幾種方法可以使slave從另一個服務開始,例如,從master拷貝數據,從另一個slave克隆,從最近的備份開始一個slave。Slave與master同步時,需要三樣東西:
(1)master的某個時刻的數據快照;
(2)master當前的日志文件、以及生成快照時的字節偏移。這兩個值可以叫做日志文件坐標(log file coordinate),因為它們確定了一個二進制日志的位置,你可以用SHOW MASTER STATUS命令找到日志文件的坐標;
(3)master的二進制日志文件。
可以通過以下幾中方法來克隆一個slave:
(1) 冷拷貝(cold copy)
停止master,將master的文件拷貝到slave;然后重啟master。缺點很明顯。
(2) 熱拷貝(warm copy)
如果你僅使用MyISAM表,你可以使用mysqlhotcopy拷貝,即使服務器正在運行。
(3) 使用mysqldump
使用mysqldump來得到一個數據快照可分為以下幾步:
<1>鎖表:如果你還沒有鎖表,你應該對表加鎖,防止其它連接修改數據庫,否則,你得到的數據可以是不一致的。如下:
mysql> FLUSH TABLES WITH READ LOCK;
<2>在另一個連接用mysqldump創建一個你想進行復制的數據庫的轉儲:
shell> mysqldump --all-databases --lock-all-tables >dbdump.db
<3>對表釋放鎖。
mysql> UNLOCK TABLES;
3、深入了解復制
3.1、基於語句的復制(Statement-Based Replication)
這種方式的優點就是實現簡單。此外,基於語句的復制的二進制日志可以很好的進行壓縮,而且日志的數據量也較小,占用帶寬少——例如,一個更新GB的數據的查詢僅需要幾十個字節的二進制日志。而mysqlbinlog對於基於語句的日志處理十分方便。
但是,基於語句的復制並不是像它看起來那么簡單,因為一些查詢語句依賴於master的特定條件,例如,master與slave可能有不同的時間。所以,MySQL的二進制日志的格式不僅僅是查詢語句,還包括一些元數據信息,例如,當前的時間戳。即使如此,還是有一些語句,比如,CURRENT USER函數,不能正確的進行復制。此外,存儲過程和觸發器也是一個問題。
另外一個問題就是基於語句的復制必須是串行化的。這要求大量特殊的代碼,配置,例如InnoDB的next-key鎖等。並不是所有的存儲引擎都支持基於語句的復制。
3.2、基於記錄的復制(Row-Based Replication)
對於一些語句,基於記錄的復制能夠更有效的工作,如:
mysql> INSERT INTO summary_table(col1, col2, sum_col3)
-> SELECT col1, col2, sum(col3)
-> FROM enormous_table
-> GROUP BY col1, col2;
假設,只有三種唯一的col1和col2的組合,但是,該查詢會掃描原表的許多行,卻僅返回三條記錄。此時,基於記錄的復制效率更高。
另一方面,下面的語句,基於語句的復制更有效:
mysql> UPDATE enormous_table SET col1 = 0;
此時使用基於記錄的復制代價會非常高。由於兩種方式不能對所有情況都能很好的處理,所以,MySQL 5.1支持在基於語句的復制和基於記錄的復制之前動態交換。你可以通過設置session變量binlog_format來進行控制。
3.3、復制相關的文件
(1)mysql-bin.index
(2)mysql-relay-bin.index
.\mysql-02-relay-bin.000017
.\mysql-02-relay-bin.000018
(3)master.info
I/O線程更新master.info文件,內容如下(我的機器上):
| .\mysql-02-relay-bin.000019 254 mysql-01-bin.000010 286 0 52813 |
(4)relay-log.info
3.4、發送復制事件到其它slave
3.5、復制過濾(Replication Filters)

4、復制的常用拓撲結構
(1) 每個slave只能有一個master;
(2) 每個slave只能有一個唯一的服務器ID;
(3) 每個master可以有很多slave;
(4) 如果你設置log_slave_updates,slave可以是其它slave的master,從而擴散master的更新。
MySQL不支持多主服務器復制(Multimaster Replication)——即一個slave可以有多個master。但是,通過一些簡單的組合,我們卻可以建立靈活而強大的復制體系結構。
4.1、單一master和多slave
在實際應用場景中,MySQL復制90%以上都是一個Master復制到一個或者多個Slave的架構模式,主要用於讀壓力比較大的應用的數據庫端廉價擴展解決方案。因為只要Master和Slave的壓力不是太大(尤其是Slave端壓力)的話,異步復制的延時一般都很少很少。尤其是自從Slave端的復制方式改成兩個線程處理之后,更是減小了Slave端的延時問題。而帶來的效益是,對於數據實時性要求不是特別Critical的應用,只需要通過廉價的pcserver來擴展Slave的數量,將讀壓力分散到多台Slave的機器上面,即可通過分散單台數據庫服務器的讀壓力來解決數據庫端的讀性能瓶頸,畢竟在大多數數據庫應用系統中的讀壓力還是要比寫壓力大很多。這在很大程度上解決了目前很多中小型網站的數據庫壓力瓶頸問題,甚至有些大型網站也在使用類似方案解決數據庫瓶頸。
如果寫操作較少,而讀操作很時,可以采取這種結構。你可以將讀操作分布到其它的slave,從而減小master的壓力。但是,當slave增加到一定數量時,slave對master的負載以及網絡帶寬都會成為一個嚴重的問題。
這種結構雖然簡單,但是,它卻非常靈活,足夠滿足大多數應用需求。一些建議:
(1) 不同的slave扮演不同的作用(例如使用不同的索引,或者不同的存儲引擎);
(2) 用一個slave作為備用master,只進行復制;
(3) 用一個遠程的slave,用於災難恢復;
大家應該都比較清楚,從一個Master節點可以復制出多個Slave節點,可能有人會想,那一個Slave節點是否可以從多個Master節點上面進行復制呢?至少在目前來看,MySQL是做不到的,以后是否會支持就不清楚了。
MySQL不支持一個Slave節點從多個Master節點來進行復制的架構,主要是為了避免沖突的問題,防止多個數據源之間的數據出現沖突,而造成最后數據的不一致性。不過聽說已經有人開發了相關的patch,讓MySQL支持一個Slave節點從多個Master結點作為數據源來進行復制,這也正是MySQL開源的性質所帶來的好處。
4.2、主動模式的Master-Master(Master-Master in Active-Active Mode)
可能有些讀者朋友會有一個擔心,這樣搭建復制環境之后,難道不會造成兩台MySQL之間的循環復制么?實際上MySQL自己早就想到了這一點,所以在MySQL的BinaryLog中記錄了當前MySQL的server-id,而且這個參數也是我們搭建MySQLReplication的時候必須明確指定,而且Master和Slave的server-id參數值比需要不一致才能使MySQLReplication搭建成功。一旦有了server-id的值之后,MySQL就很容易判斷某個變更是從哪一個MySQLServer最初產生的,所以就很容易避免出現循環復制的情況。而且,如果我們不打開記錄Slave的BinaryLog的選項(--log-slave-update)的時候,MySQL根本就不會記錄復制過程中的變更到BinaryLog中,就更不用擔心可能會出現循環復制的情形了。
主動的Master-Master復制有一些特殊的用處。例如,地理上分布的兩個部分都需要自己的可寫的數據副本。這種結構最大的問題就是更新沖突。假設一個表只有一行(一列)的數據,其值為1,如果兩個服務器分別同時執行如下語句:
在第一個服務器上執行:
mysql> UPDATE tbl SET col=col + 1;
在第二個服務器上執行:
mysql> UPDATE tbl SET col=col * 2;
那么結果是多少呢?一台服務器是4,另一個服務器是3,但是,這並不會產生錯誤。
實際上,MySQL並不支持其它一些DBMS支持的多主服務器復制(Multimaster Replication),這是MySQL的復制功能很大的一個限制(多主服務器的難點在於解決更新沖突),但是,如果你實在有這種需求,你可以采用MySQL Cluster,以及將Cluster和Replication結合起來,可以建立強大的高性能的數據庫平台。但是,可以通過其它一些方式來模擬這種多主服務器的復制。
4.3、主動-被動模式的Master-Master(Master-Master in Active-Passive Mode)
在有些應用場景中,可能讀寫壓力差別比較大,讀壓力特別的大,一個Master可能需要上10台甚至更多的Slave才能夠支撐注讀的壓力。這時候,Master就會比較吃力了,因為僅僅連上來的SlaveIO線程就比較多了,這樣寫的壓力稍微大一點的時候,Master端因為復制就會消耗較多的資源,很容易造成復制的延時。
遇到這種情況如何解決呢?這時候我們就可以利用MySQL可以在Slave端記錄復制所產生變更的BinaryLog信息的功能,也就是打開—log-slave-update選項。然后,通過二級(或者是更多級別)復制來減少Master端因為復制所帶來的壓力。也就是說,我們首先通過少數幾台MySQL從Master來進行復制,這幾台機器我們姑且稱之為第一級Slave集群,然后其他的Slave再從第一級Slave集群來進行復制。從第一級Slave進行復制的Slave,我稱之為第二級Slave集群。如果有需要,我們可以繼續往下增加更多層次的復制。這樣,我們很容易就控制了每一台MySQL上面所附屬Slave的數量。這種架構我稱之為Master-Slaves-Slaves架構
這種多層級聯復制的架構,很容易就解決了Master端因為附屬Slave太多而成為瓶頸的風險。下圖展示了多層級聯復制的Replication架構。

當然,如果條件允許,我更傾向於建議大家通過拆分成多個Replication集群來解決
上述瓶頸問題。畢竟Slave並沒有減少寫的量,所有Slave實際上仍然還是應用了所有的數據變更操作,沒有減少任何寫IO。相反,Slave越多,整個集群的寫IO總量也就會越多,我們沒有非常明顯的感覺,僅僅只是因為分散到了多台機器上面,所以不是很容易表現出來。
此外,增加復制的級聯層次,同一個變更傳到最底層的Slave所需要經過的MySQL也會更多,同樣可能造成延時較長的風險。
而如果我們通過分拆集群的方式來解決的話,可能就會要好很多了,當然,分拆集群也需要更復雜的技術和更復雜的應用系統架構。
4.5、帶從服務器的Master-Master結構(Master-Master with Slaves)
級聯復制在一定程度上面確實解決了Master因為所附屬的Slave過多而成為瓶頸的問題,但是他並不能解決人工維護和出現異常需要切換后可能存在重新搭建Replication的問題。這樣就很自然的引申出了DualMaster與級聯復制結合的Replication架構,我稱之為Master-Master-Slaves架構
和Master-Slaves-Slaves架構相比,區別僅僅只是將第一級Slave集群換成了一台單獨的Master,作為備用Master,然后再從這個備用的Master進行復制到一個Slave集群。
這種DualMaster與級聯復制結合的架構,最大的好處就是既可以避免主Master的寫入操作不會受到Slave集群的復制所帶來的影響,同時主Master需要切換的時候也基本上不會出現重搭Replication的情況。但是,這個架構也有一個弊端,那就是備用的Master有可能成為瓶頸,因為如果后面的Slave集群比較大的話,備用Master可能會因為過多的SlaveIO線程請求而成為瓶頸。當然,該備用Master不提供任何的讀服務的時候,瓶頸出現的可能性並不是特別高,如果出現瓶頸,也可以在備用Master后面再次進行級聯復制,架設多層Slave集群。當然,級聯復制的級別越多,Slave集群可能出現的數據延時也會更為明顯,所以考慮使用多層級聯復制之前,也需要評估數據延時對應用系統的影響。


























