基础信息
MySQL提供参数slave_exec_mode来控制主从复制中遇到的数据冲突和错误,有严格模式(STRICT)和冥等模式(IDEMPOTENT)两种选项,默认为严格模式。
在严格模式下,MySQL会严格检查每次需要APPLY的BINLOG和当前节点数据是否匹配,并在下列场景中抛出错误并停止复制:
- 插入操作,要插入的记录在从节点上存在主键或唯一键冲突。
- 更新操作,要更新的记录在从节点上不存在和不匹配,严格模式需在所有列上进行全部匹配。
- 删除操作,要删除的记录在从节点上不存在和不匹配,严格模式需在所有列上进行全部匹配。
参数slave_exec_mode的官网解释:
Controls how a replication thread resolves conflicts and errors during replication. IDEMPOTENT mode
causes suppression of duplicate-key and no-key-found errors; STRICT means no such suppression
takes place.
如果发生主从节点数据不一致触发复制异常,可采用:
- 通过设置参数sql_slave_skip_counter来跳过复制错误,注意参数sql_slave_skip_counter与GTID复制不兼容。
- 通过设置参数slave-skip-errors来跳过复制错误,注意参数slave-skip-errors不能动态设置需重启MySQL服务。
- 通过设置参数slave_exec_mode=IDEMPOTENT来跳过复制。
测试环境
MySQL 版本: 5.7.26-29-log Percona Server (GPL)
将MySQL从节点设置为:slave_exec_mode=IDEMPOTENT
在MySQL主节点准备测试数据:
## 删除测试表1
DROP TABLE IF EXISTS `repl_test1`;
## 创建新测试表1
CREATE TABLE `repl_test1` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`c1` int(11) DEFAULT NULL,
`c2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uqi_c1` (`c2`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 1,1,1;
## 删除测试表2
DROP TABLE IF EXISTS `repl_test2`;
## 创建新测试表2
CREATE TABLE `repl_test2` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`c2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
## 插入测试数据
INSERT INTO `repl_test2`(`id`,`c2`)SELECT 1,1;
## 刷新BINLOG日志,方便解析
FLUSH LOGS;
查看BINLOG操作日志:
mysqlbinlog -vvv mysql-bin.000xxx |egrep '###|COMMIT|BEGIN|SESSION.GTID_NEXT'
场景1
场景:主节点插入数据,从节点上已存在该数据,从节点数据和主节点数据完全相同。
先在从节点执行(制造主从数据差异):
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test2`(`id`,`c2`)SELECT 2,2;
再在主节点执行:
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test2`(`id`,`c2`)SELECT 2,2;
复制无异常报错,主从节点数据相同。
主节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:76'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:77'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test2`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:76'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:77'/*!*/;
BEGIN
COMMIT
主节点插入从节点已存在数据,从节点数据和主节点数据完全相同:
- 如果表中存在唯一索引,从节点会将INSERT操作转换为DELETE操作+INSERT操作,并保证最终主从数据相同。
- 如果表中不存在唯一索引,从节点上不会执行任何操作,并生成一个空事务,并保证最终主从数据相同。
场景2
场景:主节点插入数据,从节点上已存在该数据,从节点数据和主节点数据仅主键相同,其他列不同。
先在从节点执行(制造主从数据差异):
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,22,22;
INSERT INTO `repl_test2`(`id`,`c2`)SELECT 2,22;
再在主节点执行:
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test2`(`id`,`c2`)SELECT 2,2;
复制无异常报错,主从节点数据相同。
主节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:84'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:85'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test2`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:84'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=22 /* INT meta=0 nullable=1 is_null=0 */
### @3=22 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:85'/*!*/;
BEGIN
### UPDATE `test`.`repl_test2`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=22 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点插入从节点已存在数据,从节点数据和主节点数据仅主键相同,其他列不同:
- 如果表中存在唯一索引,从节点会将INSERT操作转换为DELETE操作+INSERT操作,并保证最终主从数据相同。
- 如果表中不存在唯一索引,从节点将INSERT操作转换为UPDATE操作,并保证最终主从数据相同。
场景3
场景:主节点插入数据,从节点上已存在该数据,从节点数据和主节点数据仅主键相同和唯一键相同,其他列不同
先在从节点执行(制造主从数据差异):
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,22;
再在主节点执行:
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
复制无异常报错,主从节点数据相同。
主节点产生BINLOG:
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:92'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:92'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=22 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点插入从节点已存在数据,从节点数据和主节点数据仅主键相同和唯一键相同,其他列不同:
- 如果表中存在唯一索引,从节点会将INSERT操作转换为DELETE操作+INSERT操作,并保证最终主从数据相同。
场景4
场景:主节点插入数据,从节点上已存在该数据,从节点数据和主节点数据主键和唯一键相互冲突,其他列相同。
在主节点插入数据,模拟更多情况:
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
先在从节点执行(制造主从数据差异):
UPDATE `repl_test1` SET `c1`=3 WHERE `id`=2;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,4,3;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 4,2,4;
主从当前状态数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
+----+------+------+
2 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 3 | 2 |
| 3 | 4 | 3 |
| 4 | 2 | 4 |
+----+------+------+
4 rows in set (0.00 sec)
再在主节点执行:
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,3,3;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 4,4,4;
复制无异常报错,主从节点数据在所操作的记录上保持相同。
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
+----+------+------+
4 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 3 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
+----+------+------+
4 rows in set (0.00 sec)
注意:从节点上在唯一键上出现数据冲突。
主节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:139'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=3 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### @3=3 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:140'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=4 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=4 /* INT meta=0 nullable=1 is_null=0 */
### @3=4 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:139'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=3 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=4 /* INT meta=0 nullable=1 is_null=0 */
### @3=3 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=3 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### @3=3 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:140'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=4 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=4 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=4 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=4 /* INT meta=0 nullable=1 is_null=0 */
### @3=4 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点插入数据,从节点上已存在该数据,从节点数据和主节点数据主键和唯一键相互冲突,其他列相同:
- 如果表中存在唯一索引,从节点会将INSERT操作转换为DELETE操作+INSERT操作,并保证主从节点在所操作的列上相同。
场景5
场景:主节点删除数据,但从节点上的数据与主节点上数据不匹配,主键和唯一键相同,其他列不同
先在从节点执行(制造主从数据差异):
UPDATE `repl_test1` SET `c2`=11 WHERE `id`=1;
UPDATE `repl_test1` SET `c2`=11 WHERE `id`=1;
再在主节点执行:
DELETE FROM `repl_test1` WHERE `id`=1;
DELETE FROM `repl_test2` WHERE `id`=1;
复制无异常报错,主从节点数据相同,数据都被删除。
主节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:99'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=1 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:100'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test2`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:99'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:100'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test2`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点删除数据,但从节点上的数据与主节点上数据不匹配,主键和唯一键相同,其他列不同:
- 无论表上是否有唯一索引键,从节点会将主键来进行删除,保证和主节点有相同主键的记录被删除。
场景6
场景:主节点删除数据,但从节点上的数据与主节点上数据不匹配,主键和唯一键相同,其他列不同
在主节点插入数据,模拟更多情况:
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,3,4;
先在从节点执行(制造主从数据差异):
UPDATE `repl_test1` SET `c1`=11,`c2`=11 WHERE `id`=1;
UPDATE `repl_test1` SET `c1`=4,`c2`=22 WHERE `id`=2;
UPDATE `repl_test1` SET `c1`=2,`c2`=33 WHERE `id`=3;
UPDATE `repl_test1` SET `c1`=3,`c2`=22 WHERE `id`=2;
再在主节点执行:
DELETE FROM `repl_test1` WHERE `id`=1;
DELETE FROM `repl_test1` WHERE `id`=2;
复制无异常报错,主从节点数据相同,数据都被删除。
主节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:115'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=1 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:116'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点产生BINLOG:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:115'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=11 /* INT meta=0 nullable=1 is_null=0 */
### @3=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:116'/*!*/;
BEGIN
### DELETE FROM `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### @3=22 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点删除数据,但从节点上的数据与主节点上数据不匹配,主键相同,唯一键和其他列不同:
- 从节点会将主键来进行删除,保证和主节点有相同主键的记录被删除。
场景7
场景:主节点删除数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度没有匹配的记录。
在主节点插入数据,模拟更多情况:
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,3,3;
先在从节点执行(制造主从数据差异):
DELETE FROM `repl_test1` WHERE `id` = 1;
DELETE FROM `repl_test2` WHERE `id` = 1;
DELETE FROM `repl_test1` WHERE `id` = 3;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 4,3,4;
此时主从数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
| 4 | 3 | 4 |
+----+------+------+
2 rows in set (0.00 sec)
再在主节点执行:
DELETE FROM `repl_test1` WHERE `id`=1;
DELETE FROM `repl_test2` WHERE `id`=1;
DELETE FROM `repl_test1` WHERE `id`=3;
复制无异常报错,主从节点数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
+----+------+------+
1 row in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
| 4 | 3 | 4 |
+----+------+------+
2 rows in set (0.00 sec)
主节点删除数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度没有匹配的记录:
- 从节点会将主键来进行删除,保证和主节点有相同主键的记录被删除。
场景8
场景:主节点更新数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度存在匹配的记录,但其他数据列不匹配。
在主节点插入数据,模拟更多情况:
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,3,3;
先在从节点执行(制造主从数据差异):
UPDATE `repl_test1` SET `c2`=111 WHERE `id` = 1;
UPDATE `repl_test2` SET `c2`=111 WHERE `id` = 1;
UPDATE `repl_test1` SET `c1`=222,`c2`=222 WHERE `id` = 2;
此时主从数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 111 |
| 2 | 222 | 222 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
再在主节点执行:
UPDATE `repl_test1` SET c2=11 WHERE `id`=1;
UPDATE `repl_test2` SET c2=11 WHERE `id`=1;
UPDATE `repl_test1` SET c2=22 WHERE `id`=2;
复制无异常报错,主从节点数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 11 |
| 2 | 2 | 22 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 11 |
| 2 | 2 | 22 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
主节点上BINLOG为:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:190'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:191'/*!*/;
BEGIN
### UPDATE `test`.`repl_test2`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:192'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=22 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点BINLOG为:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:190'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=111 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:191'/*!*/;
BEGIN
### UPDATE `test`.`repl_test2`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=111 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=11 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:192'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=222 /* INT meta=0 nullable=1 is_null=0 */
### @3=222 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=22 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
主节点更新数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度没有匹配的记录:
- 在应用主节点传递来的日志时,如果按照主键在从节点上找到对应记录,则直接按照主键来进行更新,保证主从节点在操作记录上数据一致。
场景9
场景:主节点更新数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度没有匹配的记录。
在主节点插入数据,模拟更多情况:
## 插入测试数据
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 3,3,3;
先在从节点执行(制造主从数据差异):
DELETE FROM `repl_test1` WHERE `id` = 1;
DELETE FROM `repl_test2` WHERE `id` = 1;
DELETE FROM `repl_test1` WHERE `id` = 3;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 4,3,4;
此时主从数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+------+------+
3 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
| 4 | 3 | 4 |
+----+------+------+
2 rows in set (0.00 sec)
再在主节点执行:
UPDATE `repl_test1` SET c2=111 WHERE `id`=1;
UPDATE `repl_test2` SET c2=111 WHERE `id`=1;
UPDATE `repl_test1` SET c2=333 WHERE `id`=3;
复制无异常报错,主从节点数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 111 |
| 2 | 2 | 2 |
| 3 | 3 | 333 |
+----+------+------+
3 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
| 4 | 3 | 4 |
+----+------+------+
2 rows in set (0.00 sec)
主节点上BINLOG为:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:168'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=111 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:169'/*!*/;
BEGIN
### UPDATE `test`.`repl_test2`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=111 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:170'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=3 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### @3=3 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=3 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=3 /* INT meta=0 nullable=1 is_null=0 */
### @3=333 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点BINLOG为:
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:168'/*!*/;
BEGIN
COMMIT
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:169'/*!*/;
BEGIN
COMMIT
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:170'/*!*/;
BEGIN
COMMIT
主节点更新数据,但从节点上的数据与主节点上数据不匹配,从节点在主键维度没有匹配的记录:
- 在应用主节点传递来的日志时,如果按照主键在从节点上无法找到对应记录,则跳过更新,并生成空事务的BINLOG日志。
场景10
场景:主节点在一个事务中更新多条记录,但从节点上的数据与主节点上数据不匹配,从节点在主键维度存在部分没有匹配的记录。
先在从节点执行(制造主从数据差异):
DELETE FROM `repl_test1` WHERE `id` = 1;
此时主从数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 1 |
+----+------+------+
1 row in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
Empty set (0.00 sec)
再在主节点执行:
BEGIN;
UPDATE `repl_test1` SET c2=111 WHERE `id`=1;
INSERT INTO `repl_test1`(`id`,`c1`,`c2`)SELECT 2,2,2;
COMMIT;
复制无异常报错,主从节点数据为:
## 主节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 1 | 1 | 111 |
| 2 | 2 | 2 |
+----+------+------+
2 rows in set (0.00 sec)
## 从节点数据
mysql> select * from `repl_test1`;
+----+------+------+
| id | c1 | c2 |
+----+------+------+
| 2 | 2 | 2 |
+----+------+------+
1 row in set (0.00 sec)
主节点上BINLOG为:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:199'/*!*/;
BEGIN
### UPDATE `test`.`repl_test1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=1 /* INT meta=0 nullable=1 is_null=0 */
### @3=111 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
从节点上BINLOG为:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c1387dd9-be50-11ed-844c-5cb9019a75d6:199'/*!*/;
BEGIN
### INSERT INTO `test`.`repl_test1`
### SET
### @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=2 /* INT meta=0 nullable=1 is_null=0 */
### @3=2 /* INT meta=0 nullable=1 is_null=0 */
COMMIT/*!*/;
标签:repl,slave,nullable,--,exec,meta,null,节点,###
From: https://www.cnblogs.com/gaogao67/p/17215154.html