MySQL資料庫主從復制的原理及配置方法

MySQL 的資料庫的高可用性的架構大概有以下幾種:集群,讀寫分離,主備。而後面兩種都是通過復制來實現的。下面將簡單介紹復制的原理及配置,以及一些常見的問題

一、復制的原理

MySQL 復制基於主伺服器在二進制日志中跟蹤所有對資料庫的更改(更新、刪除等等)。每個從伺服器從主伺服器接收主伺服器已經記錄到其二進制日志的保存的更新,以便從伺服器可以對其數據拷貝執行相同的更新。

將主伺服器的數據拷貝到從伺服器的一個途徑是使用LOAD DATA FROM MASTER語句。請註意LOAD DATA FROM MASTER目前隻在所有表使用MyISAM存儲引擎的主伺服器上工作。並且,該語句將獲得全局讀鎖定。

MySQL 使用3個線程來執行復制功能,其中1個在主伺服器上,另兩個在從伺服器上。當發出START SLAVE時,從伺服器創建一個I/O線程,以連接主伺服器並讓它發送記錄在其二進制日志中的語句。
主伺服器創建一個線程將二進制日志中的內容發送到從伺服器。該線程可以識別為主伺服器上SHOW PROCESSLIST的輸出中的Binlog Dump線程。
從伺服器I/O線程讀取主伺服器Binlog Dump線程發送的內容並將該數據拷貝到從伺服器數據目錄中的本地文件中,即中繼日志。
第3個線程是SQL線程,是從伺服器創建用於讀取中繼日志並執行日志中包含的更新。
有多個從伺服器的主伺服器創建為每個當前連接的從伺服器創建一個線程;每個從伺服器有自己的I/O和SQL線程。

二、復制線程的狀態

1.復制主線程的狀態

Sending binlog event to slave
二進制日志由各種事件組成,一個事件通常為一個更新加一些其它信息。線程已經從二進制日志讀取瞭一個事件並且正將它發送到從伺服器。
Finished reading one binlog; switching to next binlog
線程已經讀完二進制日志文件並且正打開下一個要發送到從伺服器的日志文件。
Has sent all binlog to slave; waiting for binlog to be updated
線程已經從二進制日志讀取所有主要的更新並已經發送到瞭從伺服器。線程現在正空閑,等待由主伺服器上新的更新導致的出現在二進制日志中的新事件。
Waiting to finalize termination
線程停止時發生的一個很簡單的狀態。

2.復制從I/O線程狀態

Connecting to master
線程正試圖連接主伺服器。

Checking master version
建立同主伺服器之間的連接後立即臨時出現的狀態。

Registering slave on master
建立同主伺服器之間的連接後立即臨時出現的狀態。

Requesting binlog dump
建立同主伺服器之間的連接後立即臨時出現的狀態。線程向主伺服器發送一條請求,索取從請求的二進制日志文件名和位置開始的二進制日志的內容。

Waiting to reconnect after a failed binlog dump request
如果二進制日志轉儲請求失敗(由於沒有連接),線程進入睡眠狀態,然後定期嘗試重新連接。可以使用–master-connect-retry選項指定重試之間的間隔。

Reconnecting after a failed binlog dump request
線程正嘗試重新連接主伺服器。

Waiting for master to send event
線程已經連接上主伺服器,正等待二進制日志事件到達。如果主伺服器正空閑,會持續較長的時間。如果等待持續slave_read_timeout秒,則發生超時。此時,線程認為連接被中斷並企圖重新連接。

Queueing master event to the relay log
線程已經讀取一個事件,正將它復制到中繼日志供SQL線程來處理。

Waiting to reconnect after a failed master event read
讀取時(由於沒有連接)出現錯誤。線程企圖重新連接前將睡眠master-connect-retry秒。

Reconnecting after a failed master event read
線程正嘗試重新連接主伺服器。當連接重新建立後,狀態變為Waiting for master to send event。

Waiting for the slave SQL thread to free enough relay log space
正使用一個非零relay_log_space_limit值,中繼日志已經增長到其組合大小超過該值。I/O線程正等待直到SQL線程處理中繼日志內容並刪除部分中繼日志文件來釋放足夠的空間。

Waiting for slave mutex on exit
線程停止時發生的一個很簡單的狀態。

3.復制從SQL線程狀態

Reading event from the relay log
線程已經從中繼日志讀取一個事件,可以對事件進行處理瞭。

Has read all relay log; waiting for the slave I/O thread to update it
線程已經處理瞭中繼日志文件中的所有事件,現在正等待I/O線程將新事件寫入中繼日志。

Waiting for slave mutex on exit
線程停止時發生的一個很簡單的狀態。

三、復制傳遞和狀態文件

從伺服器靠中繼日志來接收從主伺服器上傳回來的日志。並依靠狀態文件來記錄已經從主伺服器接收瞭哪些日志,已經恢復瞭哪些日志。

中繼日志與二進制日志的格式相同,並且可以用mysqlbinlog讀取。SQL線程執行完中繼日志中的所有事件並且不再需要之後,立即自動刪除它。可以采用–relay-log和–relay-log-index伺服器選項覆蓋默認中繼日志和索引文件名。其中索引文件名的作用是記錄目前正在使用中繼日志。

在下面的條件下將創建新的中繼日志:
1.每次I/O線程啟動時創建一個新的中繼日志。
2.當日志被刷新時;例如,用FLUSH LOGS或mysqladmin flush-logs。
3.當當前的中繼日志文件變得太大時。“太大”含義的確定方法:
max_relay_log_size,如果max_relay_log_size > 0
max_binlog_size,如果max_relay_log_size = 0
狀態文件名默認為master.info和relay-log.info。其中IO線程更新master.info文件,SQL線程更新relay-log.info文件。
文件中的行和SHOW SLAVE STATUS顯示的列的對應關系為:
master.info文件:

行 描述
1 文件中的行號
2 Master_Log_File
3 Read_Master_Log_Pos
4 Master_Host
5 Master_User
6 密碼(不由SHOW SLAVE STATUS顯示)
7 Master_Port
8 Connect_Retry
9 Master_SSL_Allowed
10 Master_SSL_CA_File
11 Master_SSL_CA_Path
12 Master_SSL_Cert
13 Master_SSL_Cipher
14 Master_SSL_Key

relay-log.info文件:

行 描述
1 Relay_Log_File
2 Relay_Log_Pos
3 Relay_Master_Log_File
4 Exec_Master_Log_Pos

當備份從伺服器的數據時,你還應備份這兩個小文件以及中繼日志文件。它們用來在恢復從伺服器的數據後繼續進行復制。如果丟失瞭中繼日志但仍然有 relay-log.info文件,你可以通過檢查該文件來確定SQL線程已經執行的主伺服器中二進制日志的程度。然後可以用 Master_Log_File和Master_LOG_POS選項執行CHANGE MASTER TO來告訴從伺服器重新從該點讀取二進制日志。當然,要求二進制日志仍然在主伺服器上。所以最好建議將自動刪除中繼日志的特性關閉,手工寫shell角本來防止空間滿的問題。

四、復制的配置步驟

1.創建專門用於復制的用戶(建議這樣做),從伺服器采用該帳戶登陸主伺服器:
GRANT REPLICATION SLAVE ON . TO ‘rep’@’%’ IDENTIFIED BY ‘logzgh’ ;
如果你計劃從從屬伺服器主機使用LOAD TABLE FROM MASTER或LOAD DATA FROM MASTER語句,你需要授予該賬戶其它權限:

授予賬戶SUPER和RELOAD全局權限。
為所有想要裝載的表授予SELECT權限。任何該 賬戶不能SELECT的主伺服器上的表被LOAD DATA FROM MASTER忽略掉。

2.將資料庫文件移到從伺服器上

情況一:若隻用到MyISAM表

mysql> FLUSH TABLES WITH READ LOCK;

(刷新所有表並且阻止其它寫入,不要退出該客戶端,以保持讀鎖有效。若退出,讀鎖就會釋放。)
比較簡單的辦法就是把數據目錄打包壓縮。

tar?cvf/home/mysql/snapshot.tar./data(在master上) tar -xvf /home/mysql/snapshot.tar (在slave上)

可能不需要同步 mysql 資料庫,因為在slave上的權限表和master不一樣。這時,解開壓縮包的時候要排除它。
同時在壓縮包中也不要包含任何日志文件,和狀態文件master.info、relay-log.info。

mysql> SHOW MASTER STATUS;
+——————+———-+————–+——————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+——————+———-+————–+——————+
| mysql-bin.000058 | 45036137 | | |
+——————+———-+————–+——————+

mysql> UNLOCK TABLES;

情況二:若用到InnoDB表

方法一:使用InnoDB Hot Backup工具。它無需在master上請求任何鎖就能做到快照的一致性,並且在後面中在slave上要用到的快照中已經記錄瞭日志文件名以及偏移位置。

方法二:記錄當前日志文件及偏移位置,在master關閉前執行:

mysql> FLUSH TABLES WITH READ LOCK;
mysql> SHOW MASTER STATUS;

盡快記下顯示結果中的日志文件及偏移位置。然後,在不解鎖的情況下關閉master,確保master上的快照和記錄的結果一致。

關閉master伺服器,$ mysqladmin -u root shutdown
拷貝 InnoDB 數據文件,日志文件,以及表結構設定文件(.frm文件)。

情況三:可以同時用於MyISAM和InnoDB表
在master上做SQL轉儲而無需如上所述備份二進制日志。運行mysqldump –master-data命令,然後把結果文件轉儲到slave上。
不過,這比拷貝二進制日志慢點。

3.修改my.cnf文件
在master上my.cnf文件:(重啟生效)

[mysqld]
log_bin
server_id=1 (值是 1 到 2^32-1 之間的正整數)

在slave上my.cnf文件:

[mysqld]
server_id=2 (ID必須和master的ID不同。若有多個slave,則每個slave都必須有唯一的id。)

配置slave的擴展選項

master_host=db-master.mycompany.com
master_port=3306
master_user=rep
master_password=freitag
master_connect_retry=60 (若master宕機或者slave連接斷開,slave會定期嘗試連接到master上,重試的間隔由該選項來控制,默認值是60秒。)
report_host=db-slave.mycompany.com
slave_net_timeout=3600 (slave默認會在3600秒後,若還沒收到來自master的數據,則會當作網絡斷開的情況來處理。)

伺服器認為master.info的優先級比配置文件my.cnf高,
第一次啟動slave時,master.info不存在,它從my.cnf中讀取選項值,然後把它們保存在master.info中。
下次重啟slave時,它隻讀取master.info的內容,而不會讀取my.cnf中的選項值。
想要使用不同的選項值,可以刪除master.info後重啟slave,或者使用CHANGE MASTER TO語句(推薦)重置選項值。

4.啟動從伺服器線程

mysqld_safe –user=mysql –skip-slave-start & (啟動MySQL伺服器,但不啟動slave)
設置master_log_file等參數
mysql> CHANGE MASTER TO MASTER_HOST=’qa-sandbox-1′,
MASTER_USER=’rep’,
MASTER_PASSWORD=’logzgh’,
MASTER_LOG_FILE=’mysql-bin.000007′,
MASTER_LOG_POS=471632;

mysql> START SLAVE;

執行這些程式後,從伺服器應連接主伺服器,並補充自從快照以來發生的任何更新。
如果你忘記設置主伺服器的server-id值,從伺服器不能連接主伺服器。

註釋:為瞭保證事務InnoDB復制設置的最大可能的耐受性和一致性,
應在主伺服器的my.cnf文件中使用innodb_flush_log_at_trx_commit=1和sync-binlog=1。

mysql> show variables; (檢查是否read-only,該選項令slave除瞭slave線程或者擁有SUPER權限用戶之外的都不能更新數據,確保slave不會接受來自其他客戶端的更新。)
mysql> show processlist; (檢查是否slave-start)

在啟動mysql的同時啟動slave:

mysqld_safe –user=mysql –read-only & (啟動MySQL伺服器,同時啟動slave的I/O線程)

mysql> SHOW SLAVE STATUSG;

5.切換slave為master,在slave上:

mysql> STOP SLAVE;
mysql> RESET MASTER;

五.復制啟動選項

–read_only
該選項讓從伺服器隻允許來自從伺服器線程或具有SUPER權限的用戶的更新。可以確保從伺服器不接受來自客戶的更新。

–replicate_do_db=db_name
告訴從伺服器隻做默認資料庫(由USE所選擇)為db_name的語句的復制。要指定多個資料庫,應多次使用該選項,每個資料庫使用一次。請註意不復制跨資料庫的語句

–replicate_do_table=db_name.tbl_name
告訴從伺服器線程隻做對指定表的復制。要指定多個表,應多次使用該選項,每個表使用一次。同–replicate-do-db對比,允許跨資料庫更新。

–replicate_ignore_db=db_name
告訴從伺服器不要復制默認資料庫(由USE所選擇)為db_name的語句。要想忽略多個資料庫,應多次使用該選項,每個資料庫使用一次。

–replicate-ignore-table=db_name.tbl_name
告訴從伺服器線程不要復制更新指定表的任何語句(即使該語句可能更新其它的表)。要想忽略多個表,應多次使用該選項,每個表使用一次。

–replicate_wild_do_table=db_name.tbl_name
告訴從伺服器線程限制復制更新的表匹配指定的資料庫和表名模式的語句。模式可以包含‘%’和‘_’通配符,與LIKE模式匹配操作符具有相同的含義。要指定多個表,應多次使用該選項,每個表使用一次。該選項可以跨資料庫進行更新。

–replicate_wild_ignore_table=db_name.tbl_name
告訴從伺服器線程不要復制表匹配給出的通配符模式的語句。要想忽略多個表,應多次使用該選項,每個表使用一次。該選項可以跨資料庫進行更新。

–replicate_rewrite_db=from_name->to_name
告訴從伺服器如果默認資料庫(由USE所選擇)為主伺服器上的from_name,則翻譯為to_name。隻影響含有表的語句

–report_host=slave_name
從伺服器註冊過程中報告給主伺服器的主機名或IP地址。該值出現在主伺服器上SHOW SLAVE HOSTS的輸出中。如果不想讓從伺服器自己在主伺服器上註冊,則不設置該值。

–report_port=slave_port
連接從伺服器的TCP/IP端口號,從伺服器註冊過程中報告給主伺服器。

–skip_slave_start
告訴從伺服器當伺服器啟動時不啟動從伺服器線程。使用START SLAVE語句在以後啟動線程。

–slave_skip_errors=[err_code1,err_code2,… | all]
通常情況,當出現錯誤時復制停止,這樣給你一個機會手動解決數據中的不一致性問題。該選項告訴從伺服器SQL線程當語句返回任何選項值中所列的錯誤時繼續復制。
例如:
–slave-skip-errors=1062,1053
–slave-skip-errors=all

六、不停機配置復制的方法

方法一:

如果你在某時間點做過主伺服器備份並且記錄瞭相應快照的二進制日志名和偏移量(通過SHOW MASTER STATUS命令的輸出),采用下面的步驟:

確保從伺服器分配瞭一個唯一的伺服器ID號。將備份文件拷到從伺服器上。在從伺服器上執行下面的語句,為每個選項填入適當的值:

mysql> CHANGE MASTER TO
-> MASTER_HOST=’master_host_name’,
-> MASTER_USER=’master_user_name’,
-> MASTER_PASSWORD=’master_pass’,
-> MASTER_LOG_FILE=’recorded_log_file_name’,
-> MASTER_LOG_POS=recorded_log_position;

4.在從伺服器上執行START SLAVE語句。

如果你沒有備份主伺服器,這裡是一個創建備份的快速程式。所有步驟都應該在主伺服器主機上執行。

發出該語句:
mysql> FLUSH TABLES WITH READ LOCK;仍然加鎖時,執行該命令(或它的變體):
shell> tar zcf /tmp/backup.tar.gz /var/lib/mysql
並拷到從伺服器上。發出該語句並且確保記錄瞭以後用到的輸出:
mysql>SHOW MASTER STATUS;釋放鎖:
mysql> UNLOCK TABLES;
方法二:

一個可選擇的方法是,轉儲主伺服器的SQL來代替前面步驟中的二進制復制。要這樣做,你可以在主伺服器上使用mysqldump –master-data,以後裝載SQL轉儲到到你的從伺服器。然而,這比進行二進制復制速度慢。

七、其他

1.不能從使用新二進制日志格式的主伺服器向使用舊二進制日志格式的從伺服器復制。

2.升級從伺服器時,應先關閉從伺服器,升級到相應5.1.x版本,然後重啟從伺服器並重新開始復制。5.1版本的從伺服器能夠讀取升級前寫入的舊的中繼日志並執行日志中包含的語句。升級後從伺服器創建的中繼日志為5.1格式。

3.必須在主伺服器和從伺服器上總是使用相同的全局字符集和校對規則(–default-character-set、–default- collation)。否則,會在從伺服器上遇到復制鍵值錯誤,因為在主伺服器的字符集中被認為是唯一的鍵值在從伺服器的字符集中可能不是唯一的。

4.Q:從伺服器需要始終連接到主伺服器嗎?
A:不,不需要。從伺服器可以宕機或斷開連接幾個小時甚至幾天,重新連接後獲得更新信息。

5.Q:我怎樣知道從伺服器與主伺服器的最新比較? 換句話說,我怎樣知道從伺服器復制的最後一個查詢的日期?
A:你可以查看SHOW SLAVE STATUS語句的Seconds_Behind_Master列的結果。

Q:我怎樣強制主伺服器阻塞更新直到從伺服器同步?
A:使用下面的步驟:在主伺服器上,執行這些語句:
mysql> FLUSH TABLES WITH READ LOCK;
mysql> SHOW MASTER STATUS;
記錄SHOW語句的輸出的日志名和偏移量。這些是復制坐標。

2.在從伺服器上,發出下面的語句,其中Master_POS_WAIT()函數的參量是前面步驟中的得到的復制坐標值:
mysql> SELECT MASTER_POS_WAIT(‘log_name’, log_offset);
SELECT語句阻塞直到從伺服器達到指定的日志文件和偏移量。此時,從伺服器與主伺服器同步,語句返回。

3.在主伺服器上,發出下面的語句允許主伺服器重新開始處理更新:
mysql> UNLOCK TABLES;
7.Q:怎樣通過復制來提高系統的性能?
A:你應將一個伺服器設置為主伺服器並且將所有寫指向該伺服器。然後根據預算配置盡可能多的從伺服器以及棧空間,並且在主伺服器和從伺服器之間分發讀取操作。你也可以用–skip-innodb、–skip-bdb、–low-priority-updates以及–delay-key- write=ALL選項啟動從伺服器,以便在從伺服器端提高速度。在這種情況下,為瞭提高速度,從伺服器使用非事務MyISAM表來代替InnoDB和 BDB表。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *