NO IMAGE

這裡分享一個MySQL主從庫因為某張表資料不一致的解決方法,我們可能會遇到這種情況:例項A和B是主從,可能由於誤操作或者某種原因導致A和B某張表資料不一致,可能就會導致主從複製停止,這時我們有很多種解決辦法,比如忽略掉某個錯誤,跳過events,設定slave_exec_mode為IDEMPOTENT,重做從庫,這些方法雖然能夠恢復主從執行,但是會導致資料不一致或者恢復成本較大,當然你也可以使用pt-table-sync修復,這裡提供一種利用pt-osc重做表的方法來恢復主從並解決一致性問題。

【模擬主從不一致場景】
表結構
show create table tb_info\G
*************************** 1. row ***************************
       Table: tb_info
Create Table: CREATE TABLE `tb_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `city` char(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
主庫上表資料
mysql> select * from tb_info;
—- ———-
| id | city     |
—- ———-
|  1 | beijing  |
|  2 | shanghai |
|  3 | shenzhen |
—- ———-
從庫上表資料
mysql> select * from tb_info;
—- ———-
| id | city     |
—- ———-
|  1 | beijing  |
|  2 | shanghai |
|  3 | shenzhen |
—- ———-
在從庫上刪除一條資料
delete from tb_info where id=3;
在主庫上刪除一條資料
delete from tb_info where id=3;
在從庫上檢視主從狀態
Last_Errno: 1032
Last_Error: Could not execute Delete_rows event on table db_sms.tb_info; Can’t find record in ‘tb_info’, Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event’s master log mysql-bin-83-3306.000018, end_log_pos 4868
主從同步已經停止,因為在主庫上刪除id為3的資料時,binlog傳到從庫執行時沒找到這條資料,所以發生了錯誤。

【解決方法】
下面來看如何恢復主從且讓資料達到一致性
先讓複製執行起來
在從庫上操作
stop slave;
set global slave_exec_mode=’IDEMPOTENT’;
start slave;
show slave status\G
在主上使用pt-osc重做表
pt-online-schema-change -h 127.0.0.1 -u root -P 3306 –alter “engine=innodb” D=db_sms,t=tb_info –charset=utf8 –execute –nocheck-replication-filter
在從庫檢視複製狀態
show slave status\G
驗證tb_info表資料是否一致
在主庫上使用pt工具進行主從資料檢測
pt-table-checksum –nocheck-replication-filters –no-check-binlog-format –recursion-method=processlist –replicate=test.checksum –databases=db_sms h=127.0.0.1,u=root,p=’123456′,P=3306
select * from test.checksum where this_crc <> master_crc or this_cnt <> master_cnt\G
確認主從一致,最後把slave_exec_mode修改為預設的STRICT
set global slave_exec_mode=’STRICT’;

【原理】
我們主要利用了pt-osc建立一個影子表的原理,這裡我們並沒有修改表結構,只是把主庫上的tb_info表重做了一遍,然後複製到從庫,從而達到資料一致的目的。

下面是pt-osc的工作原理,詳細原理和資訊請參考官方網站
<1>建立一個和要執行alter操作的表一樣的新的空表結構(是alter之前的結構)
<2>在新表執行alter table語句
<3>在原表中建立觸發器3個觸發器分別對應insert,update,delete操作
<4>以一定塊大小從原表拷貝資料到臨時表,拷貝過程中通過原表上的觸發器在原表進行的寫操作都會更新到新建的臨時表
<5>Rename原表到old表中,在把臨時表Rename為原表
<6>如果有參考該表的外來鍵,根據alter-foreign-keys-method引數的值,檢測外來鍵相關的表,做相應設定的處理
<7>預設最後將舊原表刪除

下面是在主庫上解析的binlog
# at 6251
#170407 11:00:47 server id 83330601  end_log_pos 6311 CRC32 0x5de3f8ed  Table_map: `db_sms`.`_tb_info_new` mapped to number 111
# at 6311
#170407 11:00:47 server id 83330601  end_log_pos 6373 CRC32 0x66de3a8d  Write_rows: table id 111 flags: STMT_END_F
BINLOG ‘
3wDnWBMphvcEPAAAAKcYAAAAAG8AAAAAAAEABmRiX3NtcwAMX3RiX2luZm9fbmV3AAID/gL HgLt
ONd
3wDnWB4phvcEPgAAAOUYAAAAAG8AAAAAAAEAAgAC//wBAAAAB2JlaWppbmf8AgAAAAhzaGFuZ2hh
aY063mY=
‘/*!*/;
### INSERT INTO `db_sms`.`_tb_info_new`
### SET
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=’beijing’ /* STRING(30) meta=65054 nullable=1 is_null=0 */
### INSERT INTO `db_sms`.`_tb_info_new`
### SET
###   @1=2 /* INT meta=0 nullable=0 is_null=0 */
###   @2=’shanghai’ /* STRING(30) meta=65054 nullable=1 is_null=0 */
# at 6373
#170407 11:00:47 server id 83330601  end_log_pos 6404 CRC32 0x98c172ae  Xid = 347
COMMIT/*!*/;
# at 6404
#170407 11:00:47 server id 83330601  end_log_pos 6540 CRC32 0xfaa3e0ad  Query   thread_id=21    exec_time=0     error_code=0
SET TIMESTAMP=1491534047/*!*/;
ANALYZE TABLE `db_sms`.`_tb_info_new` /* pt-online-schema-change */
/*!*/;
# at 6540
#170407 11:00:47 server id 83330601  end_log_pos 6723 CRC32 0xd5ba4074  Query   thread_id=21    exec_time=0     error_code=0
SET TIMESTAMP=1491534047/*!*/;
RENAME TABLE `db_sms`.`tb_info` TO `db_sms`.`_tb_info_old`, `db_sms`.`_tb_info_new` TO `db_sms`.`tb_info`