---
title: "Group Replication quorum 설계: 네트워크 파티션과 split-brain 방지"
description: "Group Replication의 과반수 원리와 장애 도메인 배치, 타임아웃, 격리 및 quorum 복구 조건을 운영 관점에서 정리한다."
tags: [ MySQL, 복제, 고가용성, 운영 ]
image: "mysql-report-bg.png"
published: "2026-10-30"
updated: "2026-10-30"
author: "MySQL 기술 노트"
source_url: ""
---

네트워크가 끊긴 서버는 상대 서버가 꺼졌는지, 상대편에서 여전히 정상적으로 쓰기를 처리하는지 혼자 판단할 수 없다. Group Replication의 quorum은 이 불확실성을 처리하는 규칙이다. 소수 쪽의 쓰기 진행을 멈추는 것은 불필요한 장애가 아니라, 서로 다른 두 데이터 이력이 생기는 것을 막는 안전장치다.

고가용성 설계에서는 서버 대수뿐 아니라 **어떤 장애가 몇 개의 멤버를 동시에 분리하는지**, 그리고 **강제 복구 전에 반대편의 쓰기를 어떻게 확실히 차단할지**를 정해야 한다. 이 글은 Community MySQL 8.0/8.4의 Group Replication을 기준으로 설명하며, InnoDB Cluster 관리 계층과 Aurora의 차이도 구분한다.

## 1. 과반수는 서버의 생존 수가 아니라 합의의 조건이다

Group Replication은 트랜잭션을 그룹에 전달하고 순서를 정하는 과정뿐 아니라 멤버십 변경에도 합의를 사용한다. 현재 합의된 구성의 멤버 수를 N이라고 하면, 과반수는 `FLOOR(N / 2) + 1`이다. 특정 장애 직후 남은 서버들이 기존 구성에서 합의를 진행하려면 이 수를 충족해야 한다.

같은 구성에서 두 개의 서로 겹치지 않는 집합이 동시에 과반수가 될 수는 없다. 이것이 네트워크가 양쪽으로 갈라졌을 때 양쪽이 각각 독립적으로 정상 그룹 행세를 하지 못하게 하는 기초다. 다만 숫자만 계산한다고 안전성이 생기는 것은 아니다. 정상적인 멤버십 프로토콜을 따라야 하며, 관리자가 구성 자체를 강제로 바꾸면 이 전제를 깨뜨릴 수 있다.

- 3개 멤버가 2개와 1개로 갈라지면, 2개 쪽은 과반수 조건을 만족하고 1개 쪽은 만족하지 못한다.
- 4개가 2개씩 갈라지면 어느 쪽도 과반수가 아니다. 3개에서 4개로 늘려도 기존 구성에서의 동시 장애 허용 수는 늘지 않는다.
- 5개가 3개와 2개로 갈라지면 3개 쪽만 과반수 조건을 만족한다. 2개 쪽에서 보이는 서버 수로 분모를 다시 계산하면 안 된다.

여기서 “진행 가능”은 합의를 위한 필요조건이다. 과반수가 있어도 primary 선출, 적용 오류, 디스크 고갈, flow control, 라우팅 장애 때문에 서비스가 지연되거나 중단될 수 있다. 또한 트랜잭션 순서 합의와 모든 멤버에서의 적용 완료는 같은 사건이 아니다. quorum 확보만으로 secondary의 읽기가 최신이라고 보장하지 않는다.

### 정상 탈퇴와 갑작스러운 상실을 구별한다

정상적으로 합의된 멤버십 변경은 구성과 과반수의 기준을 바꾼다. 예를 들어 충분한 멤버가 통신하는 동안 순서대로 정상 탈퇴하고 매번 새 구성을 합의하는 절차와, 여러 서버의 전원을 한꺼번에 끄는 절차는 다르다. 장애 중 자기 서버에서 보이는 `ONLINE` 행만 세어 “이제 두 대 그룹이므로 두 대면 충분하다”고 판단해서는 안 된다.

배포를 위해 서버를 순차적으로 재시작할 때도 매 단계의 새 view와 복구 상태를 확인해야 한다. 정상 탈퇴라는 이유만으로 그룹을 의도치 않게 너무 작게 만들면 다음 장애를 버틸 여유가 사라진다.

## 2. 장애 도메인을 기준으로 배치한다

장애 도메인은 같은 전원, 랙, 스위치, 가상화 호스트, 가용 영역처럼 함께 실패하거나 분리될 수 있는 범위다. 세 서버를 같은 호스트에 올리는 구성은 세 멤버의 기능 시험에는 쓸 수 있어도 호스트 장애에 대한 고가용성 설계는 아니다.

다음 SQL은 **가정한 배치의 장애 도메인 상실과 추가 멤버 미가용**을 계산한다. 나머지 영역의 서버들은 서로 통신 가능하고, 장애 직전 구성은 유지된다고 가정한다. 실제 네트워크 분리나 Group Replication 합의를 실행하는 시험은 아니다. `quorum_possible=1`도 서비스 복구 완료가 아니라 산술적인 과반수 조건 충족을 뜻한다.

```sql
WITH placement AS (
  SELECT '3_members_1_1_1' AS layout, 3 AS members, 1 AS lost
  UNION ALL SELECT '3_members_2_1', 3, 2
  UNION ALL SELECT '4_members_2_1_1', 4, 2
  UNION ALL SELECT '5_members_2_2_1', 5, 2
  UNION ALL SELECT '5_members_3_1_1', 5, 3
  UNION ALL SELECT '5_members_2_2_1_plus_1', 5, 3
)
SELECT layout, members, lost,
       FLOOR(members / 2) + 1 AS quorum_needed,
       members - lost AS survivors,
       members - lost >= FLOOR(members / 2) + 1 AS quorum_possible
FROM placement
ORDER BY layout;
```

실행 결과(MySQL 8.0.x):

```text
mysql> WITH placement AS (
    ->   SELECT '3_members_1_1_1' AS layout, 3 AS members, 1 AS lost
    ->   UNION ALL SELECT '3_members_2_1', 3, 2
    ->   UNION ALL SELECT '4_members_2_1_1', 4, 2
    ->   UNION ALL SELECT '5_members_2_2_1', 5, 2
    ->   UNION ALL SELECT '5_members_3_1_1', 5, 3
    ->   UNION ALL SELECT '5_members_2_2_1_plus_1', 5, 3
    -> )
    -> SELECT layout, members, lost,
    ->        FLOOR(members / 2) + 1 AS quorum_needed,
    ->        members - lost AS survivors,
    ->        members - lost >= FLOOR(members / 2) + 1 AS quorum_possible
    -> FROM placement
    -> ORDER BY layout;

+------------------------+---------+------+---------------+-----------+-----------------+
| layout                 | members | lost | quorum_needed | survivors | quorum_possible |
+------------------------+---------+------+---------------+-----------+-----------------+
| 3_members_1_1_1        |       3 |    1 |             2 |         2 |               1 |
| 3_members_2_1          |       3 |    2 |             2 |         1 |               0 |
| 4_members_2_1_1        |       4 |    2 |             3 |         2 |               0 |
| 5_members_2_2_1        |       5 |    2 |             3 |         3 |               1 |
| 5_members_2_2_1_plus_1 |       5 |    3 |             3 |         2 |               0 |
| 5_members_3_1_1        |       5 |    3 |             3 |         2 |               0 |
+------------------------+---------+------+---------------+-----------+-----------------+
6 rows in set (0.00 sec)
```

3개를 1·1·1로, 5개를 2·2·1로 분산하면 어느 한 영역을 잃어도 기존 구성의 과반수 조건을 유지한다. 반면 3개를 2·1로 배치하면 2개가 있는 영역의 상실을 견디지 못한다. “두 영역에 분산했다”는 설명만으로는 충분하지 않다.

`5_members_2_2_1_plus_1` 행은 5개 구성에서 2개가 있는 영역 상실과 별도 멤버 1개의 동시 미가용을 가정한다. 유지보수 중인 서버를 장애 예산에서 빼놓으면 설계가 낙관적으로 변한다. 반드시 정상 상태와 유지보수 상태를 따로 계산한다.

일반적인 Group Replication 그룹에 데이터 없는 투표 전용 witness를 추가하는 방식은 사용하지 않는다. 새로운 voting member는 정상적인 MySQL 멤버로서 데이터 복제와 그룹 참여를 감당해야 한다. 멤버를 늘리면 장애 여유와 함께 저장 공간, 복구 트래픽, 그룹 통신 비용도 늘어난다. 영역 간 지연이 큰 배치를 단순히 멤버 수 문제로 취급해서는 안 된다.

## 3. 네트워크 분리 때 실제로 일어나는 일

다음 그림은 5개 구성의 통신망이 3개와 2개로 나뉘는 개념 흐름이다. 다수 쪽의 서비스 계속 여부에는 primary와 적용 상태, 클라이언트 라우팅 등 추가 조건이 있다.

```mermaid
flowchart TD
    A[합의된 5개 멤버 구성] --> B[그룹 통신망 분리]
    B --> C[서로 통신 가능한 3개]
    B --> D[서로 통신 가능한 2개]
    C --> E[과반수 확보와 구성 재합의]
    E --> F[primary 및 적용 상태 확인]
    F --> G[유효한 쓰기 경로 유지]
    D --> H[과반수 상실로 진행 대기]
    H --> I[재연결 또는 timeout에 따른 탈퇴]
    I --> J[재가입 및 종료 동작 정책]
    H --> K[강제 복구 검토]
    K --> L[반대편 중단과 데이터 이력 확인]
    L --> M[한쪽만 복구 후 쓰기 재개]
```

### 장애 감지는 관측이지 상대편 정지의 증명이 아니다

`performance_schema.replication_group_members`는 조회한 서버의 관점에서 현재 멤버 상태를 보여준다. `UNREACHABLE`은 그 관측 지점에서 통신하지 못한다는 뜻이지, 원격 서버의 프로세스가 종료되었다는 증거가 아니다. 반대편은 과반수를 확보하고 정상적으로 운영 중일 수도 있다.

네트워크 분리는 반드시 대칭적이지도 않다. SQL 접속 경로는 열려 있지만 그룹 통신 경로만 막힐 수 있고, 운영자 PC에서는 안 보이는 서버가 애플리케이션에서는 보일 수 있다. 단일 health check나 한 서버의 멤버 목록으로 전체 토폴로지를 확정하지 않는다.

### 소수 쪽은 즉시 모든 연결이 끊기는 것이 아니다

과반수 상실 중에는 트랜잭션이 합의 진행을 기다릴 수 있다. 읽기 연결이나 관리 접속이 살아 있다는 이유로 정상 writer라고 판단하면 안 된다. 클라이언트의 타임아웃도 서버 트랜잭션 결과를 자동으로 확정하지 않는다. 응답을 못 받은 쓰기는 커밋 여부를 업무 키로 확인하고, 멱등성 기준에 따라 재시도해야 한다.

분리된 멤버의 적용이 멈추거나 재가입이 진행되는 동안 읽기가 허용될 수 있다. stale read가 허용되지 않는 업무에서는 쓰기 endpoint뿐 아니라 읽기 대상 제외 정책도 필요하다.

## 4. 서로 다른 타임아웃을 하나로 취급하지 않는다

장애 감지, 멤버 추방, 소수 멤버의 자진 탈퇴, 자동 재가입, 최종 종료 동작은 서로 다른 단계다.

- `group_replication_member_expel_timeout`: 장애 의심이 생긴 뒤 추방 전까지 기다리는 시간이다. MySQL 8.4 문서는 초기 장애 감지 구간과 이 대기 시간을 구분한다. 값을 줄인다고 과반수가 없는 그룹이 강제로 합의할 수 있게 되는 것은 아니다.
- `group_replication_unreachable_majority_timeout`: 과반수와 통신하지 못하는 멤버가 그룹을 떠나기 전 기다리는 시간이다. `0`은 즉시 탈퇴가 아니라 무기한 대기다. 유한한 시간이 지나면 대기 중인 트랜잭션을 롤백하고 오류 상태로 이행하는 경로가 작동한다.
- `group_replication_autorejoin_tries`: 해당 장애에서 자동 재가입을 시도할 횟수다. 재가입 대기 중에는 super read only 상태여도 읽기가 허용될 수 있다. 오류 종류에 따라 재가입 경로를 거치지 않는 경우도 있다.
- `group_replication_exit_state_action`: 적용되는 실패 경로에서, 재가입을 시도한다면 그 시도가 소진된 뒤 취할 동작이다. `READ_ONLY`는 `super_read_only` 설정, `OFFLINE_MODE`는 일반 클라이언트의 접속 제한과 쓰기 차단, `ABORT_SERVER`는 서버 종료로 이어진다.

따라서 “추방 timeout을 짧게 잡았으니 그 시간 안에 모든 기존 연결이 차단된다”는 가정은 틀리다. 완전히 단절된 멤버는 자신이 추방된 사실을 즉시 전달받을 수도 없다. 애플리케이션의 재시도·연결 풀 정책, 관측 주기, 재가입 정책까지 합쳐 장애 수렴 시간을 설계한다.

기본값도 버전을 구별해야 한다. 예를 들어 아래 실습의 MySQL 8.0 결과를 MySQL 8.4의 종료 동작 기본값으로 읽으면 안 된다. 8.4 공식 문서의 `group_replication_exit_state_action` 기본값은 `OFFLINE_MODE`다. 운영 환경에서는 버전과 실제 실행값을 확인하고 의도한 정책을 명시한다.

### 폐기용 서버에서 진단 객체를 확인한다

아래 첫 명령은 **Group Replication이 설치되지 않은 폐기용 실습 서버에서만 실행하는 준비 작업**이다. 운영 서버에 무조건 복사하지 않는다. 이미 플러그인이 있으면 다시 설치하지 말고 조회 부분만 실행한다. 플러그인을 로드하는 것과 그룹을 시작하는 것은 다르며, 이 실습에서는 그룹을 시작하지 않는다.

```sql
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM information_schema.PLUGINS
WHERE PLUGIN_NAME = 'group_replication';
SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM performance_schema.global_variables
WHERE VARIABLE_NAME IN (
  'group_replication_member_expel_timeout',
  'group_replication_unreachable_majority_timeout',
  'group_replication_autorejoin_tries',
  'group_replication_exit_state_action'
)
ORDER BY VARIABLE_NAME;
```

실행 결과(MySQL 8.0.x):

```text
mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';

Query OK, 0 rows affected (0.06 sec)

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
    -> FROM information_schema.PLUGINS
    -> WHERE PLUGIN_NAME = 'group_replication';

+-------------------+---------------+
| PLUGIN_NAME       | PLUGIN_STATUS |
+-------------------+---------------+
| group_replication | ACTIVE        |
+-------------------+---------------+
1 row in set (0.00 sec)

mysql> SELECT VARIABLE_NAME, VARIABLE_VALUE
    -> FROM performance_schema.global_variables
    -> WHERE VARIABLE_NAME IN (
    ->   'group_replication_member_expel_timeout',
    ->   'group_replication_unreachable_majority_timeout',
    ->   'group_replication_autorejoin_tries',
    ->   'group_replication_exit_state_action'
    -> )
    -> ORDER BY VARIABLE_NAME;

+------------------------------------------------+----------------+
| VARIABLE_NAME                                  | VARIABLE_VALUE |
+------------------------------------------------+----------------+
| group_replication_autorejoin_tries             | 3              |
| group_replication_exit_state_action            | READ_ONLY      |
| group_replication_member_expel_timeout         | 5              |
| group_replication_unreachable_majority_timeout | 0              |
+------------------------------------------------+----------------+
4 rows in set (0.00 sec)
```

너무 짧은 대기 시간은 일시적인 네트워크 흔들림이나 CPU 정체를 잦은 추방·재가입으로 확대할 수 있다. 너무 긴 시간은 애플리케이션 대기, 재시도 폭증, 오래된 읽기 노출을 늘릴 수 있다. 모든 환경에 통하는 권장 초 수를 정하기보다 실제 장애 감지와 재접속 시간을 시험해야 한다.

## 5. 상태 조회는 그룹 관측과 로컬 보호 상태를 함께 본다

다음 조회는 호스트명이나 주소를 출력하지 않고 멤버의 상태와 역할만 확인한다. 실제 운영에서는 각 서버를 구별할 수 있는 `MEMBER_ID`와 수집 시각을 내부 관측 데이터에 함께 남기고, 여러 멤버에서 같은 시점의 view를 대조한다.

```sql
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):

```text
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;

+--------------+-------------+--------------+
| MEMBER_STATE | MEMBER_ROLE | member_count |
+--------------+-------------+--------------+
| OFFLINE      |             |            1 |
+--------------+-------------+--------------+
1 row in set (0.01 sec)
```

이 글의 실습 서버는 그룹을 시작하지 않았으므로 정상적인 다중 멤버 그룹의 결과가 아니다. 여기서 관측되는 비가입 상태를 실제 네트워크 분리 결과로 해석해서는 안 된다. 운영 환경에서도 조회 자체의 성공, 플러그인의 `ACTIVE`, 그룹의 `ONLINE`, 쓰기 가능 상태는 서로 다른 사실이다.

```sql
SELECT @@version AS mysql_version,
       @@global.read_only AS read_only,
       @@global.super_read_only AS super_read_only,
       @@global.offline_mode AS offline_mode;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT @@version AS mysql_version,
    ->        @@global.read_only AS read_only,
    ->        @@global.super_read_only AS super_read_only,
    ->        @@global.offline_mode AS offline_mode;

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

이 결과는 조회한 인스턴스의 보호 설정일 뿐이다. `super_read_only=0`은 유효한 primary 또는 과반수 확보의 증명이 아니다. 반대로 `super_read_only=1`도 데이터의 최신성이나 모든 읽기 연결 차단을 뜻하지 않는다. `OFFLINE_MODE`에는 관리 권한을 가진 접속의 예외가 있으므로 업무 계정에 불필요한 관리 권한을 주지 않는다.

그룹 가입 전에 실패하는 경로에서는 `exit_state_action`이 기대대로 실행되지 않을 수 있다. 서버가 독립 실행 상태로 쓰기를 받는 상황을 줄이려면 시작 시 `super_read_only=ON` 정책을 검토하고, 그룹이 유효하게 시작된 뒤 primary의 상태 전환을 확인한다. 운영자가 임의로 읽기 전용을 해제하는 행위도 별도로 통제해야 한다.

**검증 범위:** 위 SQL은 폐기용 MySQL 8.0 서버에서 실행한 배치 계산, 플러그인 로드, 설정 및 진단 객체 조회다. 실제 다중 멤버 그룹, 패킷 차단, 추방, primary 선출, 강제 quorum 복구 또는 Aurora failover를 실행한 결과는 아니다. 운영 반영 전에는 분리된 시험 환경에서 아래 장애 절차를 별도로 검증해야 한다.

## 6. quorum 상실의 복구는 새 primary 지정부터 시작하지 않는다

quorum을 잃은 그룹은 단순히 primary를 하나 지정한다고 안전해지지 않는다. 복구의 첫 질문은 “어느 서버가 살아 있는가”가 아니라 **다른 곳에서 쓰기를 계속하는 그룹이 없는가**다.

### 1단계: 쓰기 유입과 자동화를 통제한다

애플리케이션의 쓰기 유입을 통제하고, 중복으로 동작하는 자동 승격·재시작 작업을 정지한다. 기존 연결 풀, 우회 접속, 배치 프로그램도 포함한다. 단지 로드 밸런서에서 노드를 빼는 것은 이미 연결된 세션이나 직접 접속을 차단하지 못할 수 있다.

### 2단계: 모든 장애 도메인에서 상태를 수집한다

각 영역의 프로세스 생존, 그룹 통신 상태, 멤버십, primary 역할, GTID 이력, 적용 오류를 수집한다. 단일 운영 경로로 보이지 않는 서버는 “정지”가 아니라 “미확인”으로 기록한다. 다수 쪽이 정상임을 확인했다면 소수 쪽을 강제로 새 그룹으로 만들지 말고 정상 그룹으로의 서비스 경로와 재가입을 복구한다.

### 3단계: 제외할 멤버가 쓰기를 할 수 없게 만든다

강제 구성 변경을 검토한다면 제외될 서버가 실제로 중단되었음을 확인한다. 원격 관리 경로에서 서버를 정지하고 자동 재시작도 차단하는 등, 확인 가능한 fencing 절차가 필요하다. 서로 단절된 두 운영팀이 각각 자기 쪽을 강제 복구하지 않도록 승인 주체도 하나로 정한다.

네트워크 단절 자체는 fencing의 증거가 아니다. 그 단절이 해소되었을 때 이전 서버가 다시 업무 쓰기를 받을 수 있는지까지 고려한다. 읽기 전용 변수 하나에만 의존하는 방식도 관리자의 우회나 잘못된 재시작 설정에 취약하다.

### 4단계: 기준 이력과 복구 방식을 정한다

연락 가능한 서버 중 하나를 임의로 고르지 않는다. 기존 primary에 다른 멤버가 아직 보유하지 않은 트랜잭션이 있을 수 있다. GTID의 포함 관계, 수신·적용 상태, 애플리케이션 커밋 기록을 조사하고, 제외되는 이력이 있다면 데이터 손실 가능성을 승인 절차에 포함한다. GTID 개수만 많은 서버가 항상 올바른 기준인 것은 아니다.

수동 Group Replication의 `group_replication_force_members`는 quorum 상실을 풀기 위한 최후 수단이다. 정상 primary 전환이나 일반적인 멤버 추가용 변수가 아니다. 대상 주소는 각 멤버의 `group_replication_local_address`를 기준으로 하며, 성공 후에는 값을 비워야 이후 그룹 시작을 방해하지 않는다. 이 글은 실제 강제 복구를 시험하지 않았으므로 복사 실행용 변경 SQL을 제공하지 않는다.

InnoDB Cluster에서는 AdminAPI의 `Cluster.forceQuorumUsingPartitionOf()` 경로와 해당 버전의 절차를 따른다. 이 기능도 다른 파티션이 쓰기를 계속하지 않는다는 전제와 fencing을 대신해 주지 않는다. 모든 멤버가 정지한 전체 장애의 재시작은 quorum 상실 중인 실행 그룹의 강제 구성 변경과 구별해야 한다.

### 5단계: 서비스 재개와 멤버 복귀를 분리한다

복구한 멤버들에서 일관된 멤버십과 쓰기 역할, 적용 오류 부재를 확인한 뒤 업무 쓰기를 제한적으로 재개한다. 이후 제외한 멤버의 이력 차이를 확인하고 재가입·재동기화 또는 재구축한다. 독립적으로 쓰인 두 이력이 있다면 단순 재가입으로 자동 병합된다고 가정하지 않는다. 업무 정합성 검증과 미확정 요청의 재처리까지 마쳐야 복구가 끝난다.

## 7. Aurora에는 같은 멤버 수 공식을 적용하지 않는다

Aurora는 스토리지와 컴퓨트를 분리하고, 클러스터 볼륨을 여러 가용 영역에 복제하는 구조다. Aurora reader는 Community Group Replication의 voting member가 아니므로 “reader 두 대를 추가해 세 표를 만들었다”는 식의 계산은 맞지 않는다.

Aurora의 설계에서는 writer와 reader의 영역 배치, 승격 우선순위, cluster endpoint, 연결 재시도, 실제 failover 동작을 관리형 서비스 절차로 검증한다. reader 수는 승격 후보와 읽기 처리 여유에 영향을 주지만, 이 글의 `group_replication_force_members`나 종료 동작 변수로 Aurora 클러스터를 복구하지 않는다. 로컬 MySQL 실습을 Aurora의 장애 보장값이나 복구 시간 측정으로 확장해서도 안 된다.

## 8. 설계 및 리허설 체크리스트

- [ ] 현재 합의된 멤버 수와 과반수 기준을 알고 있으며, 관측된 생존 수로 분모를 임의 축소하지 않는다.
- [ ] 호스트·스위치·가용 영역 장애마다 동시에 상실할 멤버 수를 계산했다.
- [ ] 유지보수 중 한 멤버가 빠진 상태에서도 목표 장애를 견딜 수 있는지 확인했다.
- [ ] 그룹 통신 단절, SQL 접속 단절, 서버 프로세스 종료를 서로 구별해 관측한다.
- [ ] 추방 timeout, 과반수 상실 timeout, 자동 재가입, 종료 동작의 실행값과 적용 순서를 확인했다.
- [ ] 재가입 중 오래된 읽기를 허용할지 정했고, 일반 업무 계정에 관리 접속 예외 권한을 주지 않았다.
- [ ] 장애 응답을 못 받은 쓰기의 커밋 확인과 멱등 재시도 기준이 있다.
- [ ] 강제 복구 전 제외 서버의 중단과 자동 재시작 차단을 증명할 수 있다.
- [ ] 복구 승인 주체와 기준 이력 선택, 손실 가능성 승인, 복귀 서버의 재구축 기준이 있다.
- [ ] 분리된 시험 환경에서 다수·소수 분할뿐 아니라 과반수 없는 분할, 연결 복구, 유지보수 중 장애를 시험한다.

## 결론

quorum 설계의 핵심은 홀수 서버를 맞추는 데 있지 않다. 같은 장애가 끊어 놓는 멤버 집합을 계산하고, 합의할 수 없는 쪽이 진행을 멈추도록 유지하며, 강제 복구 시 그 안전장치를 사람이 무너뜨리지 않도록 절차를 세우는 데 있다.

과반수는 데이터 이력이 갈라지지 않게 하는 기반이고, fencing과 재시도 정책은 그 기반을 실제 서비스 운영까지 연결한다. 다음 단계에서는 primary 선출과 라우팅, 장애 후 읽기 일관성을 함께 검토해 “그룹은 복구되었지만 애플리케이션은 여전히 잘못된 서버를 본다”는 상황을 줄여야 한다.

## 참고 자료

- [MySQL 8.4: 네트워크 파티션과 quorum 상실 처리](https://dev.mysql.com/doc/refman/8.4/en/group-replication-network-partitioning.html)
- [MySQL 8.4: 장애 감지와 네트워크 분리에 대한 대응](https://dev.mysql.com/doc/refman/8.4/en/group-replication-responses-failure.html)
- [MySQL 8.4: Group Replication 시스템 변수](https://dev.mysql.com/doc/refman/8.4/en/group-replication-system-variables.html)
- [MySQL Shell: quorum 상실에서 클러스터 복구](https://dev.mysql.com/doc/mysql-shell/8.4/en/restore-cluster-from-quorum-loss.html)
- [Amazon Aurora: 고가용성 구조](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html)
