Last Updated on 4월 24, 2024 by Jade(정현호)
안녕하세요
이번 글은 얼마전 작성한 RDS for MySQL MSR(Multi Source Replication) 기능에서 다룬 External Replication 과 연관된 MySQL Replication 방식 중 하나인 멱등성(idempotent)에 대해서 확인해보도록 하겠습니다.
멱등성(idempotent) 이란
멱등법칙(冪等法則) 또는 멱등성(冪等性, 영어: idempotent)은 수학이나 전산학에서 연산의 한 성질을 나타내는 것으로, 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 의미합니다. (Ref. wiki)
멱등성은 프로그래밍에서 중요한 개념입니다. 이 속성은 작업이나 기능이 여러 번 수행되더라도 결과가 항상 동일하게 유지되는 것을 의미합니다.
예를 들어서 어떤 숫자에 1을 곱하게 되면 여러 번의 연산을 수행해도 처음 1을 곱한 것과 같은 동일한 숫자가 되기 때문에 이런 것을 멱등 하다라고 할 수 있습니다.
replica_exec_mode
replica_exec_mode(slave_exec_mode) 파라미터는 MySQL의 복제 스레드가 복제 중에 충돌 및 오류를 어떻게 처리하는지를 제어하는 파라미터입니다.
MySQL 8.0.26 버전 부터는 slave_exec_mode 파라미터는 더 이상 사용되지 않으며 replica_exec_mode 으로 사용합니다.
replica_exec_mode(slave_exec_mode) 파라미터의 기본값은 NDB 클러스터에서 명시적으로 설정된 모든 값을 무시하고 항상 IDEMPOTENT로 사용합니다.
그 외 기본 MySQL에서는 STRICT을 기본 값을 사용합니다.
- IDEMPOTENT Mode: 중복 키 그리고 키 없음 오류 발생을 억제합니다.
- STRICT Mode: 복제 스레드에서 에러에 대해서 오류 발생을 억제하지 않음을 의미합니다. (오류가 발생함을 의미)
MySQL 5.7과 8.0 버전 둘 다 동일 합니다.
Default Value | IDEMPOTENT (NDB) |
STRICT (Other) |
IDEMPOTENT
Replica 인스턴스에서 replica_exec_mode(slave_exec_mode) 에서 IDEMPOTENT(멱등성)으로 설정하였을 때 키 오류에 대해서 어떻게 처리되는지 테스트해보겠습니다.
• Replica 인스턴스에서 파라미터 설정 후 조회
mysql> show variables like 'replica_exec_mode'; +-------------------+------------+ | Variable_name | Value | +-------------------+------------+ | replica_exec_mode | IDEMPOTENT | +-------------------+------------+
• 테스트 테이블 생성 및 데이터 입력
-- Source 인스턴스에서 수행 mysql> use test; mysql> CREATE TABLE t1 ( id bigint unsigned not null auto_increment, col varchar(100), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ; mysql> insert into t1(id,col) values(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E'); mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | +----+------+
중복 키 오류 테스트
Replica(slave) 인스턴스에서 먼저 데이터 입력(insert)한 다음 Source(master) 인스턴스에서 데이터를 입력하겠습니다.
• Replica 인스턴스
-- Replica 인스턴스에서 먼저 insert를 진행 mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | +----+------+ -- Replica 에서는 F1 값으로 입력함 mysql> insert into t1 (id,col) values(6,'F1'); Query OK, 1 row affected (0.04 sec) mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | | 6 | F1 | +----+------+
• Source 인스턴스
-- Source 인스턴스에서 insert 수행 mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | +----+------+ -- Source 에서는 F2 값으로 입력함 mysql> insert into t1 (id,col) values(6,'F2'); Query OK, 1 row affected (0.04 sec) mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | | 6 | F2 | +----+------+
Replica 인스턴스에서 복제 상태와 데이터를 확인해보도록 하겠습니다.
• Replica 인스턴스
-- Replica 인스턴스에서 복제 상태 확인 mysql> show replica status\G *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event <..중략..> Replica_IO_Running: Yes Replica_SQL_Running: Yes <-- 에러 없음!!! <..중략..> Seconds_Behind_Source: 0 Last_IO_Errno: 0 <-- 에러 없음!!! Last_IO_Error: Last_SQL_Errno: 0 <-- 에러 없음!!! Last_SQL_Error: <..중략..> -- Replica에서 t1 테이블 다시 조회 mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | | 6 | F2 |<!!-- F1에서 F2로 +----+------+
mysqld 로그에도 별도의 내용이 기록되지 않으며 replace into 또는 INSERT ... ON DUPLICATE KEY UPDATE 으로 처리됩니다.
replica_exec_mode의 기본 값인 STRICT 에서는 Duplicate entry .., Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY 에러가 발생합니다.
키를 찾을 수 없음 오류 테스트
Replica 인스턴스에서 먼저 데이터를 삭제한 다음 Source 인스턴스에서 해당 행에 대해서 UPDATE나 DELETE를 수행하도록 하겠습니다.
• Replica 인스턴스
mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | | 6 | F2 | +----+------+ mysql> delete from t1 where id=6; Query OK, 1 row affected (0.10 sec) mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | +----+------+
• Source 인스턴스
-- Source 인스턴스에서 update를 수행 mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | | 6 | F2 | +----+------+ mysql> update t1 set col='F1' where id=6; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from t1; +----+------+ | id | col | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | | 5 | E | +----+------+
이제 Replica 인스턴스에서 복제 상태를 확인해보도록 하겠습니다.
• Replica 인스턴스
mysql> show replica status\G *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event <..중략..> Replica_IO_Running: Yes Replica_SQL_Running: Yes <-- 에러 없음!!! <..중략..> Seconds_Behind_Source: 0 Last_IO_Errno: 0 <-- 에러 없음!!! Last_IO_Error: Last_SQL_Errno: 0 <-- 에러 없음!!! Last_SQL_Error: <..중략..>
show replica status(show slave status) 명령어를 통해서 확인할 수 있는 것처럼 복제 에러가 발생되지 않는 것을 확인할 수 있습니다.
replica_exec_mode의 기본 값인 STRICT 에서는 Can't find record in... Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND 에러가 발생합니다.
이와 같이 replica_exec_mode을 IDEMPOTENT(멱등성)으로 사용하면 기본 값 STRICT 모드에서 발생되는 키 중복 또는 키 없음 에러에 대해서 에러가 발생되지 않는다는 것을 확인할 수 있습니다.
물론 그 외 DDL(alter 및 drop 등)이나 계정 및 권한 관련된 DCL 수행에 관해서는 복제 스레드의 에러가 발생하는 것은 동일합니다.
키 중복에 대해서는 mysqld 로그에 별도로 기록이 남지 않지만, 키 없음 에러는 복제 에러가 발생되지는 않지만 mysqld 로그에는 관련된 에러 내역에 대해서 기록됩니다.
[Warning] [MY-010584] [Repl] Slave SQL for channel '': Worker 1 failed executing transaction 'ANONYMOUS' at master log binlog.000024, end_log_pos 2464; Could not execute Update_rows event on table test.t1; Can't find record in 't1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log binlog.000024, end_log_pos 2464, Error_code: MY-001032
IDEMPOTENT(멱등성) 으로 설정되어 복제가 수행되어 이와 같은 키 에러에 대해서 처리되었을 경우 입력(insert)의 경우 Replica 인스턴스에서 다른 값이 먼저 입력되어 있어도 Source 인스턴스 값으로 되기 때문에 멱등법칙으로 처리가 됩니다.
또한 Source 인스턴스에서 삭제(Delete) 하려는 키(row)가 Replica 인스턴스에서는 이미 삭제되어 HA_ERR_KEY_NOT_FOUND 오류 상황에서도 결국 같은 결과이기 때문에 큰 문제는 없습니다.
다만 Source 인스터스에서 갱신(Update)이 수행된 Key에 대해서 Replica 인스턴스에서 없는 Key(행)이라면, 이 경우는 데이터 정합성을 확인해보아야 하는 케이스라고 할 수 있습니다.
RDS의 replica_exec_mode
AWS RDS 사용 환경에서 여러 가지 이유로 External Replication을 구성해서 사용하는 경우가 있을 것입니다.
그리고 얼마전에 새롭게 추가된 RDS for MySQL MSR(Multi Source Replication) 기능을 통해서도 더 많이 External Replication 을 사용할 수 있는 환경도 되었다고 생각합니다.
RDS 중에서 RDS for MySQL 8 버전을 External Replication을 사용할 경우 주요하게 확인해봐야 할 부분이 replica_exec_mode 입니다.
RDS for MySQL 8.0 버전의 파라미터 그룹에서 replica_exec_mode(slave_exec_mode)의 기본값은 "IDEMPOTENT" 입니다.
Aurora MySQL와 RDS for MySQL 5.7 버전의 파라미터 그룹에서는 slave_exec_mode 파라미터가 존재하지 않으며 기본값은 STRICT입니다.
RDS for MySQL 8.0 버전을 통해서 이번에 새롭게 추가된 RDS for MySQL MSR 기능을 사용하려고 하거나 1:1 형태의 External Replication을 사용하는 경우 show replica status 를 통해 복제 스레드 상태를 체크했던 형태로만 모니터링을 하고 있었다면 slave_exec_mode 파라미터의 기본값이 IDEMPOTENT이라서 데이터 정합성 확인이 필요한 키를 찾을 수 없음(키 없음) 에러에 대해서는 감지하지 못하고 사용하였을 수도 있습니다.
slave_exec_mode 의 보통의 기본 값인 STRICT Mode에서는 복제 스레드의 에러가 발생하게 되고 show replica status 명령어에서 복제 에러가 확인됨으로 show replica status 나 performance_schema의 replication_applier_status_by_worker 테이블에서 오류 내용을 통해서 모니터링 알람을 받을 수 있을 것입니다.
하지만 IDEMPOTENT(멱등성) 모드에서는 HA_ERR_KEY_NOT_FOUND 에러 발생을 억제하고(에러가 발생하지 않고) 해당 내용에 대해서 로그 내용으로만 기록이 남기게 됩니다. 그래서 show replica status 등으로 복제 상태(스레드)를 모니터링 하였을 경우 에러가 발생된 것을 모를 수 있습니다. 즉, 데이터 정합성에 대해서 잠재적으로 문제가 있을 수도 있지만 모른 체로 사용할 수 있다는 것을 의미합니다. (물론 RDS의 mysqld error 로그를 별도로 모니터링 하여 알람을 받는다면, 문제를 인식하고 대처를 할 수는 있습니다.)
향후에 RDS for MySQL 8.0 버전의 replica_exec_mode(slave_exec_mode) 가 Community 버전과 동일한 정책으로 STRICT 으로 변경될 수도 있습니다. 다만 글을 작성하는 시점에서는 RDS for MySQL 8 버전의 replica_exec_mode(slave_exec_mode)의 기본값이 "IDEMPOTENT 멱등성" 이기 때문에 이런 부분을 확인해보시는 것이 좋을 것이라고 생각됩니다.
RDS for MySQL MSR(Multi Source Replication) 기능이 추가되면서 RDS for MySQL을 통한 External Replication 사용시에 IDEMPOTENT(멱등성) 과 관련되어 체크해봐야 할 부분이 있을 것으로 생각되어 글을 작성해보았고 여기서 글을 마무리하도록 하겠습니다.
Reference
Reference URL
• dev.mysql.com/sysvar_replica_exec_mode
연관된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io
왜 defualt가 strict로 왜 되어있을까요..?
기본적으로 대부분의 서비스에서 MHA로 구성하여 사용한다면 IDEMPOTENT 하였을때 이중화 안정성이 더 높아질 것 같은데요.ㅎㅎ
안녕하세요
파라미터의 기본값(default)은 제품의 특징이나 방향성 또는 개발의 방향성 등
여러가지 요건에 의해서 개발 단계에서 정해지다보니, 파라미터의 기본값에 대해서 명확한 사유를 알수 있는 것도 있을수도 있겠지만
유추로 그 이유를 예상할 수 밖는 경우도 많을 것으로 생각 됩니다.
감사합니다. 좋은 하루되세요