카테고리 : MySQL/기술노트

Group Replication single-primary와 multi-primary의 차이와 위험

Group Replication의 쓰기 모드별 실행 경로, 다중 쓰기 충돌, DDL 제약과 전환 기준을 실제 3멤버 실험으로 정리한다.

저자: MySQL 기술 노트 작성: 2026.10.31 약 13분 7,236자
다운로드

Group Replication의 쓰기 모드는 연결을 몇 대에 나눌지에 관한 옵션이 아니다. 동시에 실행되는 쓰기의 충돌을 어디에서 처리하고, 애플리케이션이 어떤 실패를 책임질 것인지를 정하는 운영 계약이다. single-primary는 쓰기 진입점을 하나로 모으고, multi-primary는 여러 멤버에서 로컬 실행을 허용한 뒤 복제·인증 과정에서 충돌을 처리한다.

일반적인 OLTP 서비스라면 single-primary를 출발점으로 삼는 편이 안전하다. multi-primary는 독립적인 쓰기 영역을 명확히 나눌 수 있고, 전체 트랜잭션 재시도와 스키마 변경 통제까지 준비된 경우에 검토할 선택지다. writer 수만 늘리면 처리량이 비례해서 증가한다는 전제로 도입해서는 안 된다.

이 글은 MySQL 8.0 이상을 대상으로 하며, 제약 사항은 MySQL 8.4 공식 매뉴얼과 대조했다. SQL 진단은 MySQL 8.0.46의 폐기용 단일 서버에서 실행했고, 모드 전환과 동시 쓰기는 별도의 MySQL 8.0.46 3멤버 그룹에서 확인했다. 장애 승격 시간, 네트워크 분리, 부하 성능 및 Aurora에서의 실행을 검증한 것은 아니다.

1. 쓰기 진입점은 달라도 모든 멤버가 변경을 적용한다

single-primary: 하나의 쓰기 실행 지점

group_replication_single_primary_mode=ON이면 한 멤버가 PRIMARY이고 나머지는 SECONDARY다. 정상 구성에서 SECONDARY에는 super_read_only=ON이 적용되어 클라이언트 쓰기를 차단한다. 복제 applier의 변경 적용까지 멈춘다는 의미는 아니다.

동시에 들어온 업무 쓰기는 PRIMARY의 InnoDB에서 실행된다. 같은 행에 대한 잠금 대기와 교착 상태는 여전히 발생할 수 있지만, 여러 서버가 서로 모르는 로컬 잠금을 잡고 쓰기를 진행하는 상황을 줄일 수 있다. Group Replication의 순서 결정과 인증·복제 절차 자체가 생략되는 것은 아니다.

PRIMARY가 그룹을 떠나면 정상적인 멤버십과 과반수 조건 아래에서 새 PRIMARY를 선출할 수 있다. 그러나 서버의 역할 변경과 애플리케이션 연결 전환은 다른 일이다. MySQL Router, 프록시, 커넥터 또는 애플리케이션이 새 writer를 찾아 연결해야 하며, 이미 끊어진 트랜잭션을 서버가 이어 주지는 않는다.

multi-primary: 여러 로컬 실행 지점, 하나의 복제 데이터 집합

group_replication_single_primary_mode=OFF이면 호환되는 여러 멤버가 쓰기를 받을 수 있다. 각 서버는 자기 InnoDB에서 트랜잭션을 실행한다. 커밋 과정에서는 변경한 키를 바탕으로 구성한 write set과 그룹의 트랜잭션 순서를 이용해 동시 변경의 호환성을 판단한다. 인증에 실패한 트랜잭션은 성공한 쓰기로 취급할 수 없다.

한 멤버에서 발생한 변경은 다른 멤버에도 적용되어야 한다. 따라서 쓰기 시작점을 나누더라도 각 멤버가 보유하는 데이터와 적용해야 할 변경이 샤드처럼 분할되지는 않는다. 느린 멤버의 적용 대기열과 flow control, 네트워크 왕복, 인증 비용이 전체 처리에 영향을 준다. 독립적인 작업의 로컬 실행을 분산하는 이점은 있을 수 있지만, 전체 쓰기 적용 비용이 사라지지는 않는다.

flowchart TB
  subgraph S[단일 쓰기 모드]
    C1[업무 쓰기] --> P[PRIMARY에서 로컬 실행]
    P --> G1[그룹 순서 결정과 인증]
    G1 --> A1[각 멤버에서 변경 적용]
  end
  subgraph M[다중 쓰기 모드]
    C2[업무 쓰기 A] --> P2[멤버 A에서 로컬 실행]
    C3[업무 쓰기 B] --> P3[멤버 B에서 로컬 실행]
    P2 --> G2[그룹 순서 결정과 인증]
    P3 --> G2
    G2 --> A2[통과한 변경을 각 멤버에 적용]
    G2 --> R[충돌한 트랜잭션 거부]
  end

그림은 논리적 단계의 요약이다. 로컬 실행·원격 적용의 잠금 충돌로도 실패할 수 있으므로, 모든 실패가 인증 단계에서만 발생한다고 읽어서는 안 된다.

2. 세 멤버에서 확인한 쓰기 허용 범위

실험은 동일 버전의 세 멤버가 모두 ONLINE인 상태에서 시작했다. 아래에서는 폐기용 서버 이름을 A, B, C로 바꾸고, 각 서버에서 수집한 세 전역 변수의 값을 한데 정리했다. 실제 운영 호스트나 인증 정보는 사용하지 않았다.

모드            멤버  single_primary_mode  enforce_update_everywhere_checks  super_read_only
single-primary  A     1                    0                                0
single-primary  B     1                    0                                1
single-primary  C     1                    0                                1
multi-primary   A     0                    1                                0
multi-primary   B     0                    1                                0
multi-primary   C     0                    1                                0

single-primary 상태에서 B에 업무 테이블의 UPDATE를 보내자 다음 오류로 거부되었다. 단순히 연결이 성공하고 SELECT가 된다는 이유로 쓰기 endpoint로 사용할 수는 없다.

ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement

이후 A에서 group_replication_switch_to_multi_primary_mode()를 호출해 정상 전환 응답을 받았고, 세 멤버의 역할이 모두 PRIMARY로 바뀌었다. 실험 종료 시 group_replication_switch_to_single_primary_mode(@@server_uuid)를 A에서 호출하여 A만 PRIMARY인 상태로 되돌아온 것도 확인했다.

이는 정상 상태에서의 모드 전환 시험이다. 운영 그룹에서 동일 함수를 즉시 실행해도 안전하다는 승인은 아니다. 특히 서로 다른 버전이 섞인 multi-primary 그룹에서는 호환성 검사로 일부 멤버가 읽기 전용에 머무를 수 있다. multi-primary 설정값만으로 모든 멤버가 writable이라고 판단하지 않는다.

3. 같은 행을 여러 writer에서 바꾸면 어떻게 되는가

로컬 UPDATE 성공은 최종 성공이 아니다

실험용 InnoDB 테이블 mode_demo.inventory에는 기본 키 id와 재고 수량 qty를 두고 (1, 10), (2, 20)을 저장했다. 그룹의 일관성 수준은 비교 조건을 명확히 하기 위해 EVENTUAL로 고정했다. 두 연결은 서로 다른 멤버에 접속했다.

아래는 두 세션의 실행 순서 기록이다. 하나의 연결에 순서대로 붙여 넣는 단일 SQL 예제가 아니다. A와 B에서 UPDATE 완료를 확인한 뒤 A를 먼저 커밋하고 B의 커밋 결과를 확인했다.

멤버 A 연결: START TRANSACTION;
멤버 A 연결: UPDATE mode_demo.inventory SET qty=qty-1 WHERE id=1;
멤버 B 연결: START TRANSACTION;
멤버 B 연결: UPDATE mode_demo.inventory SET qty=qty-2 WHERE id=1;
멤버 A 연결: COMMIT;
멤버 B 연결: COMMIT;

두 멤버는 각자의 로컬 행 잠금을 잡을 수 있었다. A의 커밋은 성공했지만 B의 커밋은 다음과 같이 실패했다. 아래 오류는 해당 실행에서 얻은 MySQL client 메시지의 본문이다.

ERROR 1180 (HY000): Got error 149 - 'Lock deadlock; Retry transaction' during COMMIT

이후 세 멤버를 각각 조회해 모두 다음 내용으로 수렴했는지 확인했다. B의 수량 차감은 커밋된 변경에 포함되지 않았다.

id  qty
1   9
2   20

이 실행에서 각 멤버의 로컬 COUNT_CONFLICTS_DETECTED는 모두 0이었다. 따라서 이 결과를 “인증 충돌 카운터가 증가한 실험”으로 제시할 수는 없다. 원격 적용과 로컬 트랜잭션 사이의 잠금 충돌에 따른 커밋 실패와 인증 단계의 충돌 집계는 같은 관측값이 아니다. 애플리케이션 오류가 발생했는데 인증 충돌 카운터가 0이라는 이유만으로 다중 쓰기 충돌 가능성을 배제해서는 안 된다.

인증 충돌 자체가 발생하는 경우도 있으며, 실패 시점·오류 표현은 경합 순서와 버전에 영향을 받는다. 운영에서는 오류 번호 하나를 외워 처리하기보다 SQLSTATE, 외부·내부 오류, 트랜잭션 종료 여부와 서버 로그를 함께 분류해야 한다.

서로 다른 행은 성공해도 쓰기 확장성을 증명하지 않는다

위 결과에서 이어서 A는 id=1을 1만큼 줄이고 B는 id=2를 2만큼 줄였다. 두 UPDATE를 각각 미커밋 상태로 만든 뒤 커밋했으며, 두 커밋이 성공하고 모든 멤버의 전체 행이 아래와 일치하는지 확인했다.

id  qty
1   8
2   18

이는 분리된 키에 대한 작은 트랜잭션이 함께 성공할 수 있음을 보여 준다. 처리량이나 지연 시간의 개선을 측정한 것은 아니다. 테넌트별 writer 고정 배치를 하더라도 공용 순번, 전체 집계, 공용 잔액, UNIQUE 키가 겹치면 실제 충돌 영역은 분리되지 않는다.

4. 업무 정합성은 write set만으로 완성되지 않는다

읽은 조건과 변경한 키를 구분한다

인증은 변경한 키의 충돌을 중심으로 동작한다. 애플리케이션이 읽은 모든 조건을 전역으로 직렬화해 주는 장치가 아니다. 예를 들어 두 트랜잭션이 각각 “당직자가 둘 이상”인지 읽고 서로 다른 사람의 당직 상태를 해제한다면, 변경 행이 다르다는 사실만으로 “최소 한 명은 당직”이라는 업무 규칙이 보장되지 않는다.

이러한 읽기-판단-쓰기 규칙은 단일 서버의 격리 수준에서도 별도로 설계해야 한다. multi-primary에서 더욱 주의할 점은 SELECT ... FOR UPDATE로 얻은 잠금이 그 연결이 붙은 서버의 로컬 잠금이라는 것이다. 다른 멤버가 같은 문장을 실행하지 못하게 하는 전역 잠금으로 해석해서는 안 된다. gap lock, LOCK TABLES, GET_LOCK() 역시 그룹 전체의 직렬화 장치가 아니다.

업무상 공유 자원을 표현하는 공통 행이나 UNIQUE 제약, 명확한 소유권 라우팅 등을 검토할 수 있다. 그러나 공통 행을 추가하면 그 행은 새로운 경합 지점이 된다. 다중 쓰기 환경에서는 충돌 시 전체 트랜잭션을 다시 판단·실행하는 절차까지 포함해야 한다.

재시도는 SQL 한 문장이 아니라 업무 단위로 한다

재시도 가능한 실패라면 트랜잭션을 확실히 정리한 뒤 새 트랜잭션에서 읽기와 조건 검사를 다시 수행한다. 마지막 UPDATE만 반복하면 앞에서 읽은 값이나 다른 변경과의 관계가 깨질 수 있다. 재시도 횟수·전체 시간의 상한과 지연을 두고, 동일한 핫 키에 대한 즉시 무한 재시도를 피한다.

COMMIT 응답을 받기 전에 연결이 끊겼다면 커밋 여부가 불명확하다. 이때는 업무 요청 식별자와 UNIQUE 제약 등을 통해 처리 여부를 조회하는 경로가 필요하다. 외부 결제·메시지 발행까지 무조건 다시 실행하는 정책은 데이터베이스 내부 재시도보다 더 큰 중복 부작용을 만든다.

5. multi-primary에서 더 엄격하게 확인할 제약

  • 엄격 검사: group_replication_enforce_update_everywhere_checks는 multi-primary에서 일반적으로 ON으로 운영한다. single-primary에서는 OFF여야 한다. 검사를 끄는 것은 지원되지 않는 업무 패턴을 안전하게 바꾸는 해결책이 아니다.
  • SERIALIZABLE: multi-primary의 엄격 검사에서는 해당 격리 수준의 트랜잭션 커밋이 거부된다. 전역 직렬화를 얻기 위한 간단한 우회책으로 선택할 수 없다.
  • 연쇄 외래 키: CASCADE를 포함한 다단계 외래 키 의존성은 다중 쓰기에서 감지되지 않는 충돌과 불일치 위험을 만든다. 외래 키가 있다는 사실만이 아니라 연쇄 변경 경로까지 조사한다.
  • DDL과 다른 멤버의 DML: 한 멤버에서 스키마를 변경하는 동안 다른 멤버가 같은 객체에 쓰는 구성을 지원되는 안전한 패턴으로 보아서는 안 된다. 변경이 모든 멤버에 반영될 때까지 해당 객체의 DDL·DML을 같은 서버로 제한하는 등의 통제가 필요하다.
  • 격리 수준: 공식 문서는 multi-primary에서 REPEATABLE READ 의미에 의존하지 않는다면 READ COMMITTED를 권장한다. 로컬 gap lock과 분산 충돌 감지의 차이를 줄이기 위한 권고이지, 업무 정합성을 자동 보장한다는 뜻은 아니다.

Atomic DDL은 각 서버의 DDL 커밋·롤백 원자성에 관한 기능이다. 이것을 여러 writer에서의 스키마 변경과 동시 업무 쓰기를 자유롭게 섞어도 된다는 보장으로 확대해서는 안 된다. DDL은 암묵적 커밋 경계도 만들므로, 업무 트랜잭션 안에 넣어 함께 되돌리는 전략도 성립하지 않는다.

6. 설정값·멤버 역할·실제 쓰기 가능 여부를 함께 진단한다

다음 SQL은 폐기용 단일 MySQL 8.0.46 서버에서 객체와 문법을 검증했다. 이 서버에는 Group Replication 플러그인을 설치하지 않았으므로 일부 결과가 비어 있다. 앞 절의 3멤버 실행 결과와 환경을 구분해서 읽어야 한다. 실제 그룹에서는 각 멤버에 접속하여 같은 조회를 수행하고 차이를 비교한다.

플러그인과 로컬 읽기 전용 상태

SELECT VERSION() AS mysql_version,
       @@global.read_only AS read_only,
       @@global.super_read_only AS super_read_only;
SELECT COUNT(*) AS active_gr_plugins
FROM information_schema.PLUGINS
WHERE PLUGIN_NAME = 'group_replication'
  AND PLUGIN_STATUS = 'ACTIVE';

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@global.read_only AS read_only,
    ->        @@global.super_read_only AS super_read_only;

+---------------+-----------+-----------------+
| mysql_version | read_only | super_read_only |
+---------------+-----------+-----------------+
| 8.0.46        |         0 |               0 |
+---------------+-----------+-----------------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) AS active_gr_plugins
    -> FROM information_schema.PLUGINS
    -> WHERE PLUGIN_NAME = 'group_replication'
    ->   AND PLUGIN_STATUS = 'ACTIVE';

+-------------------+
| active_gr_plugins |
+-------------------+
|                 0 |
+-------------------+
1 row in set (0.00 sec)

active_gr_plugins=0이면 플러그인이 활성화되지 않은 환경이다. 반대로 ACTIVE라고 해도 그룹에 가입해 ONLINE이라는 뜻은 아니다. super_read_only=0 하나만으로 정상 writer를 판정하는 것도 잘못이다.

모드와 멤버 역할

SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM performance_schema.global_variables
WHERE VARIABLE_NAME IN (
  'group_replication_single_primary_mode',
  'group_replication_enforce_update_everywhere_checks',
  'group_replication_consistency'
)
ORDER BY VARIABLE_NAME;
SELECT MEMBER_STATE, MEMBER_ROLE, COUNT(*) AS member_count
FROM performance_schema.replication_group_members
GROUP BY MEMBER_STATE, MEMBER_ROLE
ORDER BY MEMBER_STATE, MEMBER_ROLE;

실행 결과(MySQL 8.0.x):

mysql> SELECT VARIABLE_NAME, VARIABLE_VALUE
    -> FROM performance_schema.global_variables
    -> WHERE VARIABLE_NAME IN (
    ->   'group_replication_single_primary_mode',
    ->   'group_replication_enforce_update_everywhere_checks',
    ->   'group_replication_consistency'
    -> )
    -> ORDER BY VARIABLE_NAME;

+-------------------------------+----------------+
| VARIABLE_NAME                 | VARIABLE_VALUE |
+-------------------------------+----------------+
| group_replication_consistency | EVENTUAL       |
+-------------------------------+----------------+
1 row in set (0.00 sec)

mysql> SELECT MEMBER_STATE, MEMBER_ROLE, COUNT(*) AS member_count
    -> FROM performance_schema.replication_group_members
    -> GROUP BY MEMBER_STATE, MEMBER_ROLE
    -> ORDER BY MEMBER_STATE, MEMBER_ROLE;

Empty set (0.01 sec)

단일 검증 서버에서는 group_replication_consistency=EVENTUAL 한 행만 보였고, 모드·엄격 검사 변수는 나오지 않았다. 일관성 변수의 존재 자체는 플러그인 활성이나 그룹 가입의 증거가 아니다. 멤버 조회의 Empty set 역시 모드가 OFF이거나 정상 그룹의 멤버가 0명이라는 뜻이 아니라, 이 환경에서 가입된 그룹 상태를 제공하지 않는다는 의미다. 실제 그룹에서는 single-primary의 ONLINE PRIMARY가 하나인지, SECONDARY 수가 예상과 맞는지 확인한다. multi-primary에서는 역할뿐 아니라 멤버별 읽기 전용 상태와 버전 호환성까지 대조한다.

로컬 인증·적용 대기와 충돌 집계

SELECT COUNT_TRANSACTIONS_IN_QUEUE AS cert_queue,
       COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS apply_queue,
       COUNT_CONFLICTS_DETECTED AS certification_conflicts
FROM performance_schema.replication_group_member_stats
WHERE MEMBER_ID = @@server_uuid;

실행 결과(MySQL 8.0.x):

mysql> SELECT COUNT_TRANSACTIONS_IN_QUEUE AS cert_queue,
    ->        COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS apply_queue,
    ->        COUNT_CONFLICTS_DETECTED AS certification_conflicts
    -> FROM performance_schema.replication_group_member_stats
    -> WHERE MEMBER_ID = @@server_uuid;

Empty set (0.00 sec)

인증 대기와 원격 트랜잭션 적용 대기를 분리해 본다. 충돌 수는 누적 카운터이므로 재시작·재가입 등 관측 구간 경계를 고려하여 증가량을 비교한다. 이 조회는 로컬 멤버로 범위를 제한했다. 모든 멤버에서 각각 수집하고 애플리케이션의 커밋 실패율·재시도율·지연과 함께 해석한다.

앞선 실험처럼 클라이언트 커밋 오류가 있어도 인증 충돌 카운터는 증가하지 않을 수 있다. 또한 Empty set을 충돌 수 0으로 변환해 대시보드에 넣으면 모니터링 자체가 없는 상황을 정상으로 오인한다. 행의 존재 여부를 먼저 검사해야 한다.

7. 읽기 일관성과 모드 전환은 별도의 계약이다

multi-primary에서 A의 커밋 응답 직후 B로 연결을 바꾸면 B의 적용 진행 상태에 따라 오래된 값을 읽을 수 있다. single-primary에서도 SECONDARY로 읽기를 분산하면 같은 종류의 문제가 생긴다. ONLINE은 적용 지연이 항상 0이라는 표시가 아니다.

group_replication_consistency는 이러한 대기 정책을 조정한다. BEFORE는 실행 전에 앞선 변경의 로컬 적용을 기다리는 방향이고, AFTER는 쓰기의 변경이 다른 ONLINE 멤버에 적용될 때까지 기다리는 방향이다. 더 강한 정책은 지연과 느린 멤버의 영향을 함께 늘릴 수 있다. 이미 시작한 트랜잭션의 오래된 스냅샷까지 무조건 최신으로 바꾸는 설정으로 해석하지 않는다.

single-primary 승격에서는 BEFORE_ON_PRIMARY_FAILOVER가 새 PRIMARY에 남은 이전 변경의 적용을 기다리게 한다. MySQL 8.4 문서는 이를 기본값으로 설명하지만, 실제 운영에서는 버전·전역값·세션값을 확인해야 한다. 이 글의 3멤버 실험은 EVENTUAL을 명시했으며 승격 시 대기 동작을 시험하지 않았다.

모드를 변경할 때는 실행 중인 서버마다 변수를 제각각 바꾸거나 읽기 전용을 강제로 해제하지 않는다. 지원되는 그룹 모드 전환 함수 또는 InnoDB Cluster로 관리되는 환경의 관리 절차를 사용한다. 다음 순서로 변경 경계를 만든다.

  1. 현재 모드, ONLINE 멤버, 버전, 적용 대기열과 장기 트랜잭션을 기록한다.
  2. 업무 쓰기를 통제하고 DDL·배치 작업을 중지하거나 변경과 겹치지 않게 조정한다.
  3. 새 모드의 라우팅·연결 풀·재시도 정책이 준비되었는지 확인한다.
  4. 모드를 전환하고 모든 멤버의 역할, 모드 변수, 엄격 검사, 읽기 전용 상태를 다시 읽는다.
  5. 새 연결로 소규모 업무 쓰기와 다른 멤버의 읽기를 확인한 뒤 트래픽을 단계적으로 연다.
  6. 재시작 시 사용하는 설정 및 관리 도구의 상태까지 대조한다. 현재 함수 호출 성공을 재시작 후 동일 상태의 증명으로 취급하지 않는다.

multi-primary에서 single-primary로 돌아갈 때는 기존 연결이 이전 writer를 계속 사용하는지 특히 확인한다. 서버 역할은 정상인데 연결 풀에 남은 쓰기 세션이 SECONDARY를 향하면서 오류가 증가할 수 있다.

8. Aurora MySQL에는 모드 이름을 그대로 대응시키지 않는다

일반적인 Aurora DB 클러스터는 하나의 writer와 같은 클러스터 볼륨에 연결하는 reader로 구성된다. Group Replication의 독립 InnoDB 인스턴스와 write set 인증 구조를 그대로 사용하는 모델이 아니다. Aurora reader를 Group Replication의 투표 멤버나 추가 primary로 계산해서는 안 된다.

Aurora에서 쓰기를 다른 진입점으로 전달하는 기능이 있더라도, 그것을 독립 writer들이 동시에 인증하는 multi-primary와 같은 것으로 해석하지 않는다. Aurora의 지원되는 failover·endpoint·일관성·파라미터 절차를 따라야 하며 Group Replication 모드 변경 함수를 운영 처방으로 옮겨서는 안 된다.

다만 애플리케이션이 책임지는 부분은 공통적이다. 연결 재수립, 커밋 결과 불명확성 처리, 요청 멱등성, 쓰기 후 읽기 경로는 관리형 데이터베이스에서도 필요하다. 이 글의 로컬 3멤버 결과는 Aurora의 장애 전환이나 복제 지연을 측정한 결과가 아니다.

9. 선택과 도입 전 점검표

다음에 해당하면 single-primary를 우선한다.

multi-primary를 검토하려면 다음을 먼저 증명한다.

정리

single-primary는 쓰기의 실행 지점을 모아 복잡성을 줄이고, multi-primary는 여러 실행 지점을 허용하는 대신 충돌·라우팅·DDL·재시도의 책임을 넓힌다. 세 멤버 실험에서도 single-primary의 SECONDARY는 쓰기를 거부했고, multi-primary에서는 두 로컬 UPDATE가 완료되어도 같은 행을 바꾼 한 트랜잭션이 COMMIT에서 실패했다. 반대로 서로 다른 행의 변경은 모두 커밋되어 전 멤버에 수렴했다.

운영 판단의 기준은 “쓰기 가능한 서버가 몇 대인가”가 아니라 “업무가 요구하는 정합성을 유지하면서 실패와 지연을 제어할 수 있는가”다. 다음 단계에서는 이 모드 구분을 바탕으로 quorum 상실, 네트워크 분리와 PRIMARY 선출 시 쓰기 경계를 살펴본다.

참고 문서