摘要:
在主從復制的架構中,正常情況下主上操作的記錄也會在從上進行操作,雖說是異步復制,但操作會“實時”的同步到從。比如在主上不小心誤操作了,還沒等反應過來從上也會馬上執行誤操作,后期只有通過二進制或則備份恢復數據了,費時,又費力,沒有任何回旋的余地,而且也會影響到網站的功能。而pt-slave-delay故意讓主上的操作延遲制定的時間寫入到從,這樣就可以快速的處理上面說的問題了。下面介紹下使用方法,其實挺簡單的。
前提:
下載地址:wget www.percona.com/downloads/percona-toolkit/2.2.2/percona-toolkit-2.2.2.tar.gz
安裝方法:perl Makefile.PL;make;make install
使用方法:
pt-slave-delay [OPTIONS] SLAVE_DSN [MASTER_DSN]
執行該命令鏈接數據庫的賬號需要有 PROCESS、REPLICATION CLIENT、and SUPER權限。他是通過Slave的relay log(中繼日志)的position(偏移量),不斷啟動,關閉 replication SQL thread 來保持主從一直延時固定的時間來實現的,所以沒有必要鏈接主服務器。如果想在運行中停止的話,按CTRL+C中斷就可以了。具體執行的命令:
pt-slave-delay --delay=1m --interval=15s --run-time=10m u=root,p=123456,h=192.168.200.25,P=3307
#--delay :從庫延遲主庫的時間,上面為1分鍾。
#--interval :檢查的間隔時間,上面為15s檢查一次。(可選),不選則1分鍾檢查一次。
#--run-time :該命令運行時間,上面為該命令運行10分鍾關閉。(可選),不選則永遠運行。
#--ask-pass :隱性輸入密碼。
其他的為鏈接數據庫的賬號信息,賬號要有PROCESS, REPLICATION CLIENT, and SUPER權限
注意:延遲的時間實際為 delay+interval,即該命令的讓從延遲主75s。
還有一些具體的參數請見:http://www.percona.com/doc/percona-toolkit/2.2/pt-slave-delay.html
背景:

主:
root@localhost : aaa 02:37:21>select * from test;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
從:
root@192.168.200.25 : aaa 03:11:44>select * from test;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
root@192.168.200.25 : aaa 03:12:05>show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.200.25
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 1919
Relay_Log_File: zhoujy-relay-bin.000131
Relay_Log_Pos: 1636
Relay_Master_Log_File: mysql-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: aaa
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 1919
Relay_Log_Space: 1951
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID:
Master_Info_File: /opt/mysql/mysql5.6/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
執行:
session1:命令
zhoujy@zhoujy:~$ pt-slave-delay --delay=1m --interval=15s --run-time=10m --ask-pass u=root,h=192.168.200.25,P=3307 Enter password for 192.168.200.25: 2013-10-23T15:16:40 slave running 0 seconds behind 2013-10-23T15:16:40 STOP SLAVE until 2013-10-23T15:17:40 at master position mysql-bin.000003/1919 2013-10-23T15:16:55 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:10 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:25 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:40 no new binlog events
session2:主
root@localhost : aaa 03:17:34>insert into test(name) values('d'),('e'); Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0 root@localhost : aaa 03:17:44>select * from test; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
session3:從
root@192.168.200.25 : aaa 03:17:31>select * from test; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | +----+------+ 3 rows in set (0.00 sec)
從session3里面看到,數據還沒有同步過來,因為在session2里插入了數據,繼續看session1的輸出日志:
zhoujy@zhoujy:~$ pt-slave-delay --delay=1m --interval=15s --run-time=10m --ask-pass u=root,h=192.168.200.25,P=3307 Enter password for 192.168.200.25: 2013-10-23T15:16:40 slave running 0 seconds behind 2013-10-23T15:16:40 STOP SLAVE until 2013-10-23T15:17:40 at master position mysql-bin.000003/1919 2013-10-23T15:16:55 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:10 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:25 slave stopped at master position mysql-bin.000003/1919 2013-10-23T15:17:40 no new binlog events 2013-10-23T15:17:55 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:18:10 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:18:25 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:18:40 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:18:55 START SLAVE until master 2013-10-23T15:17:55 mysql-bin.000003/2142 2013-10-23T15:19:10 slave running 0 seconds behind
在日志里面看到了,在15:17:55的時候檢測到了新的事件,在15:18:55的時候進行了同步,數據差不多在15:17:40寫進去的,時間點剛好一致。15s后檢測到了,1m后同步到從。
繼續看從上的數據:
root@192.168.200.25 : aaa 03:18:57>select * from test; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
最后命令運行了10m后,自動結束:
2013-10-23T15:25:40 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:25:55 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:26:10 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:26:25 no new binlog events 2013-10-23T15:26:40 slave stopped at master position mysql-bin.000003/2142 2013-10-23T15:26:40 Setting slave to run normally
原理:
這個過程的實現:
pt-slave-delay –delay=10m 開始運行
(a)連接到從服務器
檢測從服務器落后主服務器多少秒$behind,並輸出。
然后記錄當前時間,以及Slave_IO_Thread獲取到的master_log_file與read_master_log_pos做為一個對象,保存到某數組@positions中。
情況一:
如果從服務器的Slave_SQL_Thread正在運行,並且$behind小於10分鍾, STOP SLAVE SQL_THREAD,並計算出多長時間之后(now()-$behind+10分鍾),
才需要再次START SLAVE。再次START的時間點稱為$next_start。等待一分鍾,再goto(a)
情況二:
如果從服務器的Slave_SQL_Thread正在運行,並且$behind超出10分鍾, 那就什么也不做。
情況三:
如果從服務器的Slave_SQL_Thread已經被STOP了,並且當前時間沒有達到$next_start, 那就等待一分鍾,再goto(a)
情況四:
如果從服務器的Slave_SQL_Thread已經被STOP了,並且當前時間達到$next_start,說明從服務器已經休息夠了,
就從數組@positions中挑選一個合適的對象出來,此對象記錄的時間要比較接近當前時間的10分鍾之前。
然后START SLAVE SQL_THREAD UNTIL 此對象的master_log_file與read_master_log_pos。等待一分鍾,goto(a)
總結:
通過上面的方法很好的解決了主的誤操作影響到從,前提是從允許延遲一定的時間。那如何解決呢?當在主上執行了誤操作之后,回到從上
執行:
slave stop;
再執行:
MASTER_LOG_FILE = 'log_name
', MASTER_LOG_POS = log_pos
Pos通過mysqlbinlog查看二進制日志獲得。
這樣就跳過了錯誤,最后通過切換主從或則把從的表覆蓋到主上(需要關閉應用)讓主的誤操作的數據恢復,和通過binlog和備份比大大減少了恢復時間。
在5.6里面已經包含了延遲這個功能:
CHANGE MASTER TO MASTER_DELAY = N ...;