1. 程式人生 > >MySQL複製 slave_exec_mode 引數IDEMPOTENT 說明 MySQL Binlog 【ROW】和【STATEMENT】選擇

MySQL複製 slave_exec_mode 引數IDEMPOTENT 說明 MySQL Binlog 【ROW】和【STATEMENT】選擇

 

 

背景:

      今天無意當中看到引數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個event

  2、最好記的是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是不支援的 -

[email protected]: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
[email protected]: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的冥等行為是通過主鍵來判斷備機是否有重複值從而產生覆寫操作,如果表沒有主鍵,則備機即使設了冥等也可能會比主機多重複資料。




 

背景:

      今天無意當中看到引數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個event

  2、最好記的是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是不支援的 -

[email protected]: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
[email protected]: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的冥等行為是通過主鍵來判斷備機是否有重複值從而產生覆寫操作,如果表沒有主鍵,則備機即使設了冥等也可能會比主機多重複資料。