【轉】如何使用slave_exec_mode優雅的跳過1032 1062的複製錯誤 MySQL Binlog 【ROW】和【STATEMENT】選擇

今天線上的主從複製發生1062的錯誤,使用sql_slave_skip_counter跳過以後,因爲後面的事務須要對剛剛的數據進行update,後續形成了新的1032的錯誤。html

後來,無心中發現還有更好的方式跳過1032 和1062錯誤的方式,而且比skip 的方式更好。mysql

 

背景:

      今天無心當中看到參數slave_exec_mode,從手冊裏的說明看出該參數和MySQL複製相關,是能夠動態修改的變量,默認是STRICT模式(嚴格模式),可選值有IDEMPOTENT模式(冪等模式)。設置成IDEMPOTENT模式可讓從庫避免1032(從庫上不存在的鍵)和1062(重複鍵,須要存在主鍵或則惟一鍵)的錯誤,該模式只有在ROW EVENT的binlog模式下生效,在STATEMENT EVENT的binlog模式下無效。IDEMPOTENT模式主要用於多主複製和NDB CLUSTER的狀況下,其餘狀況不建議使用。從上面的介紹來看,這個參數的讓從庫跳過指定的錯誤,那問題來了:sql

1:和 sql_slave_skip_counter 比,有什麼好處?數據庫

2:和 slave-skip-errors = N比,有什麼好處?安全

帶着這2個問題,本文來進行相關的測試和說明。 post

環境:

MySQL版本:Percona MySQL 5.7測試

複製模式:ROW,沒有開啓GTIDurl

測試:

① 1062 錯誤:Could not execute ... event on table db.x; Duplicate entry 'xx' for key 'PRIMARY', Error_code: 1062;spa

主從上的測試表結構:設計

CREATE TABLE `x` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

主從上的表記錄:

M:

複製代碼
select * from x;
+----+
| id |
+----+
|  2 |
|  3 |
+----+
2 rows in set (0.01 sec)
複製代碼

S:

複製代碼
select * from x;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)
複製代碼

主從上的表記錄原本就不一致了,主上缺乏了id=1的記錄。

此時從上的slave_exec_mode爲默認的STRICT模式:

複製代碼
show variables like 'slave_exec_mode';
+-----------------+--------+
| Variable_name   | Value  |
+-----------------+--------+
| slave_exec_mode | STRICT |
+-----------------+--------+
1 row in set (0.00 sec) 
複製代碼

M上的binlog模式爲:

show variables like 'binlog_format';                                                                                                            +---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

在M上執行:

insert into x values(1),(4),(5);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

由於從上已經存在了id=1的記錄,此時從的複製就報了1062的錯誤:

Last_SQL_Errno: 1062
Last_SQL_Error: Could not execute Write_rows event on table dba_test.x; Duplicate entry '1' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin-3306.000006, end_log_pos 7124

出現這個錯誤時,你們的一致作法就是執行:sql_slave_skip_counter=N。關於該參數的說明能夠看MySQL小誤區:關於set global sql_slave_skip_counter=N 命令的一些點。文章的總結是:

複製代碼
  一、set global sql_slave_skip_counter=N中的N是指跳過N個event

  二、最好記的是N被設置爲1時,效果跳過下一個事務。

  三、跳過第N個event後,位置若恰好落在一個事務內部,則會跳過這整個事務

  四、一個insert/update/delete不必定只對應一個event,由引擎和日誌格式決定
複製代碼

sql_slave_skip_counter的單位是「event」,不少人認爲該參數的單位是「事務」,實際上是錯誤的,由於一個事務裏包含了多個event,跳過N個可能仍是在同一個事務當中。對於上面出現1062的錯誤,把N設置成1~4效果是同樣的,都是跳過一個事務。由於執行的SQL生成了4個event:

複製代碼
show binlog events in 'mysql-bin-3306.000006' from 6950;
+-----------------------+------+------------+-----------+-------------+---------------------------------+
| Log_name              | Pos  | Event_type | Server_id | End_log_pos | Info                            |
+-----------------------+------+------------+-----------+-------------+---------------------------------+
| mysql-bin-3306.000006 | 6950 | Query      |       169 |        7026 | BEGIN                           |
| mysql-bin-3306.000006 | 7026 | Table_map  |       169 |        7074 | table_id: 707 (dba_test.x)      |
| mysql-bin-3306.000006 | 7074 | Write_rows |       169 |        7124 | table_id: 707 flags: STMT_END_F |
| mysql-bin-3306.000006 | 7124 | Xid        |       169 |        7155 | COMMIT /* xid=74803 */          |
+-----------------------+------+------------+-----------+-------------+---------------------------------+
4 rows in set (0.00 sec)
複製代碼

因此處理該錯誤的方法有:

1:skip_slavesql_slave_skip_counter

複製代碼
stop slave;                                                                                                                                     Query OK, 0 rows affected (0.00 sec)

set global sql_slave_skip_counter=[1-4];
Query OK, 0 rows affected (0.00 sec)

start slave;
Query OK, 0 rows affected (0.00 sec)
複製代碼

2:在配置文件裏指定slave-skip-errors=1062(須要重啓)

這2種方法都能讓複製恢復正常,可是會讓主從數據不一致(謹慎使用),讓從庫丟失了id=4和5的記錄。而且第2種方法還須要重啓數據庫,這時本文介紹的slave_exec_mode參數就派上用場了。在從庫上設置該參數

複製代碼
set global slave_exec_mode='IDEMPOTENT';
Query OK, 0 rows affected (0.00 sec)

stop slave;                                                                                                                                     Query OK, 0 rows affected (0.00 sec)

start slave;
Query OK, 0 rows affected (0.00 sec)
複製代碼

一樣在主上執行:

insert into x values(1),(4),(5);

能夠驚喜的發現主從數據是同步的,沒有出現複製異常:

複製代碼
M:
select * from x;                                                                                                                                +----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

S:
select * from x;                                                                                                                                +----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.01 sec)
複製代碼

上面的測試能夠看到,參數設置成slave_exec_mode='IDEMPOTENT' 後,能夠跳過出一個錯誤的event。

② 1032錯誤:Could not execute ... event on table db.x; Can't find record in 'x', Error_code: 1032;

這個錯誤的出現是由於ROW模式下的複製,對數據的一致性有了很嚴的要求,具體的能夠看MySQL Binlog 【ROW】和【STATEMENT】選擇

主從上的測試表結構:

CREATE TABLE `x` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

主從上的表記錄:

M:

複製代碼
select * from x;                                                                                                                                +----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)
複製代碼

S:

複製代碼
select * from x;
+----+
| id |
+----+
|  1 |
|  3 |
+----+
2 rows in set (0.00 sec)
複製代碼

主從上的表記錄原本就不一致了,從上缺乏了id=2的記錄。此時從上的slave_exec_mode爲默認的STRICT模式:

複製代碼
show variables like 'slave_exec_mode';
+-----------------+--------+
| Variable_name   | Value  |
+-----------------+--------+
| slave_exec_mode | STRICT |
+-----------------+--------+
1 row in set (0.00 sec) 
複製代碼

M上的binlog模式爲:

show variables like 'binlog_format';                                                                                                            +---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

在M上執行:

BEGIN;
INSERT INTO x SELECT 4;
DELETE FROM x WHERE id = 2;
INSERT INTO x SELECT 5;
COMMIT;

由於從上不存在了id=2的記錄,此時從的複製就報了1032的錯誤:

Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute Delete_rows event on table dba_test.x; Can't find record in 'x', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin-3306.000006, end_log_pos 12102

一樣的,在上面測試中說明的2種方法可讓複製正常,可是數據也同樣會丟失。丟失了id=4和5的記錄,繼續在從庫上設置該參數:

複製代碼
set global slave_exec_mode='IDEMPOTENT';
Query OK, 0 rows affected (0.00 sec)

stop slave;                                                                                                                                     Query OK, 0 rows affected (0.00 sec)

start slave;
Query OK, 0 rows affected (0.00 sec)
複製代碼

在M上執行一樣的操做:

BEGIN;
INSERT INTO x SELECT 4;
DELETE FROM x WHERE id = 2;
INSERT INTO x SELECT 5;
COMMIT;

也能夠驚喜的發現主從數據是同步的,沒有出現複製異常。

注意:slave_exec_mode='IDEMPOTENT'不能對DDL操做冪等,而且也不能對字段長度不一樣致使的錯誤進行冪等,如把例子中的從庫表的id字段類型int改爲bigint。而且只能在binlog_format爲ROW的模式下使用,並且只能對1032和1062進行冪等模式。

總結:

      對於上面的測試總結針對slave_exec_mode參數,它能夠跳過1062和1032的錯誤,而且不影響同一個事務中正常的數據執行。若是是多個SQL組成的事務,則能夠跳過有問題的event

      看着這個參數很不錯,但手冊上說明不建議在普通的複製環境中開啓。對於NDB之外的存儲引擎,只有在肯定能夠安全地忽略重複鍵錯誤和沒有鍵的錯誤時,才應使用IDEMPOTENT模式。這參數是專門針對NBD Cluster進行設計的,NBD Cluster模式下,該參數只能設置成IDEMPOTENT模式。因此要根據本身的應用場景來決定,正常狀況下,主從是一致的,有任何錯誤發生都要報錯,不過在作特殊處理時,能夠臨時開啓。

      另外在GTID模式下的複製,sql_slave_skip_counter是不支持的,該模式下的複製能夠自行測試。

 

原文連接:

http://www.cnblogs.com/zhoujinyi/p/8035413.html

相關文章
相關標籤/搜索