参数)slave_exec_mode
slave_exec_mode = STRICT
MySQL主从同步中,经常遇到的三种同步错误:
1、在master上delete一条记录,若在slave上没有这条数据,会出现报错。
2、在master上insert一条记录,若在slave上主键值已经存在,会出现报错。
3、在master上update一条记录,若在slave上没有这条数据,会出现报错。
如果slave的my.cnf已经配置了slave_skip_errors参数,则可以自动跳过报错,如果没有配置该参数,则可以用以下方法解决:
mysql> set global slave_exec_mode='idempotent';
默认值是strict(严格模式),IDEMPOTENT 模式跳过主键冲突错误和更新删除行不存在错误。
参数)slave_skip_errors选项有四个可用值,分别为: off,all,ErorCode,ddl_exist_errors 。
默认情况下该参数值是off,我们可以列出具体的error code,也可以选择all,mysql5.6及MySQL Cluster NDB 7.3以及后续版本增加了参数ddl_exist_errors,该参数包含一系列error code(1007,1008,1050,1051,1054,1060,1061,1068,1094,1146)
一些error code代表的错误如下:
1007: 数据库已存在,创建数据库失败
1008: 数据库不存在,删除数据库失败
1050: 数据表已存在,创建数据表失败
1051: 数据表不存在,删除数据表失败
1054: 字段不存在,或程序文件跟数据库有冲突
1060: 字段重复,导致无法插入
1061: 重复键名
1068: 定义了多个主键
1094: 位置线程ID
1146: 数据表缺失,请恢复数据库
1053: 复制过程中主服务器宕机
1062: 主键冲突 Duplicate entry '%s' for key %d
测试:slave_exec_mode = STRICT 不设置slave_skip_errors参数
场景1)在主库插入一条数据,然后从库删除这条数据,然后主库再删除这条数据,从库会报错:
Last_SQL_Error: Could not execute Delete_rows event on table mytest1.log_test; Can't find record in 'log_test', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000026, end_log_pos 1136
场景2)在主库创建一个主库表,然后在从库先插入一条数据,然后再主库插入这条数据,从库会报错:
Last_SQL_Error: Could not execute Write_rows event on table mytest1.log_test_new; Duplicate entry '1' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000026, end_log_pos 2859
场景3)在主库插入一条数据,然后从库删除这条数据,然后主库再更新这条数据,从库会报错:
Last_SQL_Error: Could not execute Update_rows event on table mytest1.log_test; Can't find record in 'log_test', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000026, end_log_pos 2509
测试:slave_exec_mode = STRICT 设置slave_skip_errors参数为ALL
场景1)在主库插入一条数据,然后从库删除这条数据,然后主库再删除这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 4514
场景2)在主库创建一个主库表,然后在从库先插入一条数据,然后再主库插入这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 5195
场景3)在主库插入一条数据,然后从库删除这条数据,然后主库再更新这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 5876
测试:slave_exec_mode = idempotent 不设置slave_skip_errors参数
[root@localhost:mytest1]>show variables like '%slave_skip_errors%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| slave_skip_errors | OFF |
+-------------------+-------+
1 row in set (0.01 sec)
[root@localhost:mytest1]>select count(*) from log_test1 where id>80000000;^C
[root@localhost:mytest1]>show variables like '%slave_exec_mode%';
+-----------------+------------+
| Variable_name | Value |
+-----------------+------------+
| slave_exec_mode | IDEMPOTENT |
+-----------------+------------+
1 row in set (0.00 sec)
场景1)在主库插入一条数据,然后从库删除这条数据,然后主库再删除这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 6194
场景2)在主库创建一个主库表,然后在从库先插入一条数据,然后再主库插入这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 6875
场景3)在主库插入一条数据,然后从库删除这条数据,然后主库再更新这条数据,从库不会报错,Read_Master_Log_Pos看到会执行完这个sql
Master_Log_File: mysql-bin.000026
Read_Master_Log_Pos: 7556
下面我们来做一组关于slave_exec_mode='idempotent' 更详细的测试
mysql> create table test_idempotent(id int not null primary key,name varchar(10));
Query OK, 0 rows affected (0.02 sec)
[root@localhost:mytest1]>select * from test_idempotent;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
+----+----------+
4 rows in set (0.00 sec)
1.从库删除id=1的行,主库再删除id=1的行,slave复制不会报错
[root@localhost:mytest1]>delete from test_idempotent where id=1;
Query OK, 1 row affected (0.01 sec)
2。从库删除id=2的行,主库再更新id=2的行,slave复制不会报错
[root@localhost:mytest1]>delete from test_idempotent where id=2;
Query OK, 1 row affected (0.01 sec)
3.从库插入id=1的行,主库再插入id=1的行,slave复制不会报错,而且从库将原id=1的行删除,然后插入了主库的行
[root@localhost:mytest1]>insert into test_idempotent values(1,'zhangsan');
Query OK, 1 row affected (0.01 sec)
主库 [root@localhost:mytest1]>insert into test_idempotent values(1,'zhangsan88');
Query OK, 1 row affected (0.01 sec)
从库:
[root@localhost:mytest1]>select * from test_idempotent;
+----+------------+
| id | name |
+----+------------+
| 1 | zhangsan88 |
| 3 | wangwu |
| 4 | zhaoliu |
+----+------------+
3 rows in set (0.00 sec)
而我们再次在从库手动插入id=1的行时,会报主键冲突
[root@localhost:mytest1]>insert into test_idempotent values(1,'zhangsan');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
4.从库插入id=2的行,主库再更新id=1的行成为id=2,slave复制不会报错,而且会跳过这个sql
目标: [root@localhost:mytest1]>insert into test_idempotent values (2,'lisi');
Query OK, 1 row affected (0.01 sec)
[root@localhost:mytest1]>select * from test_idempotent;
+----+------------+
| id | name |
+----+------------+
| 1 | zhangsan88 |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
+----+------------+
4 rows in set (0.00 sec)
主库:
mysql> select * from test_idempotent;
+----+------------+
| id | name |
+----+------------+
| 1 | zhangsan88 |
| 3 | wangwu |
| 4 | zhaoliu |
+----+------------+
3 rows in set (0.00 sec)
mysql> update test_idempotent set id=2 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
从库:
[root@localhost:mytest1]>select * from test_idempotent;
+----+------------+
| id | name |
+----+------------+
| 1 | zhangsan88 |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
+----+------------+
4 rows in set (0.00 sec)