背景:

今天无意当中看到参数slave_exec_mode,从手册里的说明看出该参数和MySQL复制相关,是可以动态修改的变量,默认是STRICT模式(严格模式),可选值有IDEMPOTENT模式(幂等模式)。设置成IDEMPOTENT模式可以让从库避免1032(从库上不存在的键)和1062(重复键,需要存在主键或则唯一键)的错误,该模式只有在ROW EVENT的binlog模式下生效,在STATEMENT EVENT的binlog模式下无效。IDEMPOTENT模式主要用于多主复制和NDB CLUSTER的情况下,其他情况不建议使用。从上面的介绍来看,这个参数的让从库跳过指定的错误,那问题来了:

1:和 sql_slave_skip_counter 比,有什么好处?

2:和 slave-skip-errors = N比,有什么好处?

带着这2个问题,本文来进行相关的测试和说明。

环境:

MySQL版本:Percona MySQL 5.7

复制模式:ROW,没有开启GTID

测试:

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

主从上的测试表结构:

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

主从上的表记录:

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 命令的一些点。文章的总结是:

  1、set global sql_slave_skip_counter=N中的N是指跳过N个event2、最好记的是N被设置为1时,效果跳过下一个事务。3、跳过第N个event后,位置若刚好落在一个事务内部,则会跳过这整个事务4、一个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=utf8mb4;

主从上的表记录:

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是不支持的 -

root@localhost:3306.sock [(none)]>set sql_slave_skip_counter=1;
ERROR 1229 (HY000): Variable 'sql_slave_skip_counter' is a GLOBAL variable and should be set with SET GLOBAL
root@localhost:3306.sock [(none)]>set global sql_slave_skip_counter=1;
ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction

slave_exec_mode=IDEMPOTENT 在MySQL复制环境中是个很有用的参数:只要在备机运行set global slave_exec_mode=IDEMPOTENT ,备机的sql thread就运行在冥等模式下,可以让备机在insert主键、唯一键冲突,update、delete值未找到错误发生时不断开复制而保持冥等性(当即生效,连slave的sql线程都不用重启哟);而类似sql_slave_skip_counter=Nslave-skip-errors = N 这样的粗暴跳过错误方法可能破坏主备一致性。但官方文档的描述很简洁,我一直好奇slave_exec_mode=IDEMPOTENT 是如何在复制出错时保持一致性的--譬如主键冲突时是简单跳过还是覆写,今天在Percona 5.7下做了个实验(binlog是row格式),实验过程就省略了,直接总结如下:

1.insert场景
此时insert into语句在备机的效果就跟replace into一样,但却并不是把insert into转换成replace into来执行,分两种情况:
a.MySQL配置成autocommit,直接一条insert into ...
如这样的insert
insert into test set c1='a',c2='b';

此时insert into语句在备机执行时假如遇到主键冲突就先转化为delete再insert

delete from test where c1='old_value' and c2='old_value';
insert into test set c1='a',c2='b'; 

假如遇到非主键的唯一键冲突就转换为update
update test set set c1='a',c2='b' where c1='old_value' and c2='old_value';

b.当显示开始事务时(begin...insert into...commit;)
如这样的sql

begin;
......
insert into test set c1='a',c2='b';
......
commit;

此时begin...commit里的insert into语句在备机执行时假如遇到主键冲突、唯一键冲突都是先转化为delete再insert

begin;
......
delete from test where c1='old_value' and c2='old_value';
insert into test set c1='a',c2='b'; ...... commit; 

2.update场景
当备机不存在要更新的记录,这条update跳过不执行

3.delete场景
同update场景一样,备机跳过此delete啥也不干

注意:使用冥等模式时表要有主键
冥等模式并不是万能的,除了不能对DDL操作冥等,对字段长度不同导致的错误也不是冥等(譬如主机一个字段是char(20)而备机是char(10)),还有一个限制就是表有主键才会对insert的冥等设置有效:因为insert的冥等行为是通过主键来判断备机是否有重复值从而产生覆写操作,如果表没有主键,则备机即使设了冥等也可能会比主机多重复数据。

转载于:https://www.cnblogs.com/DataArt/p/10229657.html

MySQL复制 slave_exec_mode 参数IDEMPOTENT 说明相关推荐

  1. MySQL 主从幂等复制slave_exec_mode=IDEMPOTENT

    MySQL slave_exec_mode 参数用于控制主从复制数据冲突时的处理策略,可选值有STRICT和IDEMPOTENT,分别代表严格模式和幂等模式,默认值为STRICT,该参数可动态调整. ...

  2. MySQL复制跳过错误--slave_skip_errors、sql_slave_skip_counter、slave_exec_mode

    MySQL主从复制中的跳过错误处理参数--slave_skip_errors.sql_slave_skip_counter.slave_exec_mode 跳过复制错误--slave_skip_err ...

  3. mysql timestamp 当前_技术分享 | MySQL 复制那点事 - Seconds_behind_Master 参数调查笔记

    作者:戴骏贤 网易游戏 技术部资深数据库系统工程师. 本文来源:原创投稿 *爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源. 文章阅读时间约 15 分钟,文中著名参考文献在 ...

  4. mysql replicate_do_db_replicate-do-db参数引起的MySQL复制错误及处理办法

    replicate-do-db配置在MySQL从库的my.cnf文件中,可以指定只复制哪个库的数据.但是这个参数有个问题就是主库如果在其他的schema环境下操作,其binlog不会被从库应用,从而出 ...

  5. mysql 基于gtid复制_深入MySQL复制(二):基于GTID复制

    相比传统的MySQL复制,gtid复制无论是配置还是维护都要轻松的多.本文对gtid复制稍作介绍. 1.gtid基本概念 传统的基于binlog position复制的方式有个严重的缺点:如果slav ...

  6. mySQL 教程 第16章 MySQL复制

    第16章 MySQL复制 复制解决的问题 概述:你的网站访问量非常大,对系统的稳定性非常高,那么可以使用mysql功能的复制功能,复制是指将主要的数据库的DDL和DML操作通过二进制日志传到复制服务器 ...

  7. MySQL 复制滞后怎么办?其实方法很简单

    参考:http://database.51cto.com/art/201503/467024.htm    http://blog.itpub.net/29272216/viewspace-12446 ...

  8. 盘点几种MySQL复制的解决方案和常见的错误理解

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 作者 | 阿飞的博客 来源 | 公众号「阿飞的博客」 本文将回顾MySQL复制概念和MySQ ...

  9. 涂抹mysql笔记-mysql复制特性

    涂抹mysql笔记-mysql复制特性 mysql复制特性:既可以实现整个服务(all databases)级别的复制,也可以只复制某个数据库或某个数据库中的某个指定的表对象.即可以实现A复制到B(主 ...

最新文章

  1. 目标检测--Object Detection via Aspect Ratio and Context Aware
  2. python_day9线程、进程和协程
  3. 从零单排学Redis【白银】
  4. Springboot测试类的编写
  5. mysql的or能去重吗_mysql中bit_count和bit_or函数的含义
  6. react js 之生命周期
  7. python网课推荐-python网课什么平台好
  8. svn up (svn update) 状态缩写含义
  9. 适应adblock plus 规则的简单正则表达式匹配
  10. gephi用安装java吗_Gephi安装
  11. pdf复制文字时对于回车的处理
  12. 基于供需优化算法的函数寻优及工程优化应用
  13. android访问服务器405,Http 405 错误
  14. Python——简化表达
  15. 是什么原因才导致网站打开速度慢?
  16. UVA - 1389 Hard Life【分数规划+最小割】【最大权闭合图】
  17. 真的有必要定义VO,BO,PO,DO,DTO吗?
  18. 怎么拯救一个不大靠谱的数据库系统 (5 不靠谱的前生)
  19. 安装Apache服务
  20. TPM1.2到TPM 2.0的变化

热门文章

  1. 等保合规 等保监测 等保合规评测
  2. 博客添加音乐插件、网站运行时间、文章阅读次数和网站访客统计
  3. Dynamics crm2013 IFD部署后启用多组织
  4. ThingsBoard 添加指定客户首页展示仪表板功能
  5. Managed Beans
  6. 康考迪亚大学应用计算机科学,康考迪亚大学专业设置详情
  7. 数据库事务Transaction)那点事
  8. SystemUi通知栏
  9. mysql 字符集 表情_MySQL字符集(表情包)
  10. android禁止弹出保存此图片,安卓手机相册总是出现陌生图片?教你彻底清除!...