Skip to content
Published on

분산 시스템 핵심 개념 완전 정리: CAP 정리부터 합의 알고리즘, 일관성 모델까지

Authors

목차

1. 왜 분산 시스템인가

1.1 분산 시스템의 필요성

단일 서버로는 현대 서비스의 요구를 충족할 수 없습니다. 분산 시스템이 필요한 세 가지 핵심 이유가 있습니다.

확장성(Scalability): 트래픽이 증가하면 단일 서버의 CPU, 메모리, 디스크에는 한계가 있습니다. 수평 확장(Scale-Out)으로 여러 서버에 부하를 분산합니다.

가용성(Availability): 서버 하나가 죽어도 서비스가 계속 동작해야 합니다. 넷플릭스, 구글 같은 서비스는 99.99% 이상의 가용성을 목표로 합니다.

내결함성(Fault Tolerance): 네트워크 단절, 디스크 장애, 데이터센터 화재 등 장애는 반드시 발생합니다. 분산 시스템은 이런 장애를 견딜 수 있도록 설계됩니다.

1.2 분산 시스템의 도전 과제

단일 서버                    분산 시스템
+-----------+               +-----+  +-----+  +-----+
|  App +DB  |    vs         | N1  |--| N2  |--| N3  |
+-----------+               +-----+  +-----+  +-----+
                                  \    |    /
                               네트워크(불안정)

분산 시스템은 다음과 같은 고유한 문제를 가집니다:

  • 네트워크 지연: 노드 간 통신은 ms~s 단위의 지연이 발생
  • 부분 장애: 일부 노드만 죽고 나머지는 정상 동작
  • 시계 동기화 불가: 각 노드의 물리적 시계가 미세하게 다름
  • 비잔틴 장애: 노드가 잘못된 데이터를 보낼 수 있음

2. CAP 정리

2.1 CAP 정리란?

2000년 Eric Brewer가 제안하고, 2002년 Seth Gilbert와 Nancy Lynch가 증명한 정리입니다.

분산 시스템은 다음 세 가지 속성 중 최대 두 가지만 동시에 보장할 수 있습니다:

속성설명
Consistency (일관성)모든 노드가 같은 시점에 같은 데이터를 봄
Availability (가용성)모든 요청이 (성공/실패) 응답을 받음
Partition Tolerance (분할내성)네트워크 분할이 발생해도 시스템이 동작

2.2 왜 셋 다 못 가지나 — 직관적 증명

네트워크 분할 발생!

  [Node A]  ----X----  [Node B]
  data=1                data=1

  Client가 Node Awrite(data=2) 요청

  선택지 1 (CP): Node B와 동기화 불가 → 요청 거부 (가용성 포기)
  선택지 2 (AP): 일단 Node A만 업데이트 → 불일치 허용 (일관성 포기)

네트워크 분할(P)은 분산 시스템에서 피할 수 없으므로, 실질적으로 CP vs AP 중 선택해야 합니다.

2.3 CP 시스템 vs AP 시스템

특성CP 시스템AP 시스템
분할 시 동작쓰기 거부 또는 대기쓰기 허용, 나중에 병합
예시ZooKeeper, etcd, SpannerCassandra, DynamoDB, Riak
적합 사례금융 거래, 리더 선출소셜미디어, 쇼핑 카트

2.4 PACELC 확장

CAP의 한계를 보완하는 PACELC 모델:

if (Partition) then
    Availability vs Consistency
else
    Latency vs Consistency
시스템P 발생 시정상 시
CassandraPA (가용성)EL (낮은 지연)
SpannerPC (일관성)EC (일관성)
MongoDBPA (가용성)EC (일관성)

3. 일관성 모델

3.1 일관성 수준 비교

강한 순서대로:

엄격한 일관성 (Strict)
선형화 가능성 (Linearizability)
순차 일관성 (Sequential)
인과 일관성 (Causal)
최종 일관성 (Eventual)

3.2 Strong Consistency (강한 일관성)

모든 읽기가 가장 최근 쓰기의 결과를 반환합니다.

시간 →
Client A:  write(x=1) ------>
Client B:              read(x) → 반드시 1 반환
  • 구현: 모든 노드에 동기 복제 후 응답
  • 장점: 프로그래밍 모델이 단순
  • 단점: 높은 지연, 낮은 가용성

3.3 Sequential Consistency (순차 일관성)

모든 노드가 동일한 순서로 연산을 봅니다. 단, 실시간 순서와 다를 수 있습니다.

실제 시간:  A:write(x=1)  B:write(x=2)
순차 일관성: B:write(x=2), A:write(x=1)허용
            (모든 노드가 이 순서에 동의하면)

3.4 Causal Consistency (인과 일관성)

인과 관계가 있는 연산은 순서가 보장됩니다. 인과 관계가 없는 연산은 순서가 다를 수 있습니다.

A가 글 작성 → B가 댓글 작성 (인과 관계 있음)
  → 모든 노드에서 글이 댓글보다 먼저 보임

C가 글 작성 / D가 별도 글 작성 (인과 관계 없음)
  → 노드마다 순서가 다를 수 있음

3.5 Eventual Consistency (최종 일관성)

업데이트가 중단되면 최종적으로 모든 노드가 같은 값을 갖게 됩니다.

시간 →
Node A:  x=1 ......... x=1 (변경 전파) → x=1
Node B:  x=0 ......... x=0 → x=1 (약간의 지연 후)
Node C:  x=0 ......... x=0 → x=1
  • DNS, CDN 캐시, Amazon S3 등에서 사용
  • 충돌 해결 전략 필요: Last-Write-Wins, CRDT, 애플리케이션 수준 병합

3.6 일관성 모델 비교표

모델지연가용성프로그래밍 난이도사용 사례
Strong높음낮음쉬움금융, 재고
Sequential중간중간중간분산 락
Causal낮음높음중간소셜 피드
Eventual매우 낮음매우 높음어려움DNS, 캐시

4. 복제 (Replication)

4.1 Leader-Follower 복제

가장 일반적인 복제 방식입니다.

           쓰기
Client[Leader] → 복제 → [Follower 1]
[Follower 2]
[Follower 3]
           읽기
Client[Follower 1, 2, 3 중 아무나]

동기 복제: 리더가 모든 팔로워의 확인을 받은 후 응답. 강한 일관성, 높은 지연.

비동기 복제: 리더가 즉시 응답, 팔로워는 나중에 복제. 낮은 지연, 데이터 손실 가능.

반동기 복제: 최소 1개 팔로워의 확인 후 응답. MySQL, PostgreSQL 기본 방식.

# 반동기 복제 의사코드
def write(data):
    leader.write(data)
    ack_count = 1  # 리더 자신
    for follower in followers:
        if follower.replicate(data):
            ack_count += 1
            if ack_count >= 2:  # 최소 1개 팔로워 확인
                return SUCCESS
    return FAILURE

4.2 Multi-Leader 복제

여러 노드가 동시에 쓰기를 받을 수 있습니다.

[Leader A] ←→ [Leader B] ←→ [Leader C]
    ↓              ↓              ↓
[Follower]    [Follower]    [Follower]
  • 사용 사례: 다중 데이터센터, 오프라인 작업(Google Docs)
  • 핵심 문제: 쓰기 충돌 해결
    • Last-Write-Wins (LWW): 타임스탬프가 큰 쓰기가 승리
    • 병합: CRDT(Conflict-free Replicated Data Type) 사용
    • 사용자 해결: 충돌을 사용자에게 보여주고 선택하게 함

4.3 Leaderless 복제 (Quorum)

리더가 없고, 클라이언트가 여러 노드에 동시에 읽기/쓰기합니다.

         write(x=1)
Client[Node A][Node B][Node C]  (장애)

W=2 성공 → 쓰기 성공!

         read(x)
Client[Node A] x=1
[Node B] x=1
[Node C] x=0 (오래된 값)

R=2 → 최신 (x=1) 반환

Quorum 공식: W + R > N이면 최신 데이터 읽기 보장

설정NWR특성
강한 일관성322일관성 보장
빠른 쓰기313쓰기 빠름, 읽기 느림
빠른 읽기331쓰기 느림, 읽기 빠름
  • Cassandra, DynamoDB, Riak에서 사용
  • 안티 엔트로피(Anti-entropy), 읽기 복구(Read Repair)로 불일치 해결

5. 파티셔닝 (Partitioning / Sharding)

5.1 왜 파티셔닝인가

데이터가 단일 노드 용량을 초과하면 여러 노드에 분산 저장합니다.

5.2 해시 파티셔닝

hash(key) mod N = partition_number

key="user_123" → hash → 77 mod 3 = 1Partition 1
key="user_456" → hash → 22 mod 3 = 2Partition 2
  • 장점: 데이터 균등 분배
  • 단점: 범위 쿼리 비효율적, 노드 추가/제거 시 대량 재배치

5.3 범위 파티셔닝

Partition 0: A-F
Partition 1: G-M
Partition 2: N-S
Partition 3: T-Z
  • 장점: 범위 쿼리 효율적
  • 단점: 핫스팟 발생 가능 (특정 범위에 데이터 집중)

5.4 Consistent Hashing

노드 추가/제거 시 최소한의 키만 재배치합니다.

        0
       / \
     330   30
     /       \
   300        60
   |    Ring    |
   270        90
     \       /
     240   120
       \ /
       180

Node A: 0-90
Node B: 90-180
Node C: 180-270
Node D: 270-360

Node E 추가(135 위치)Node B90-135Node E로 이동
  • 가상 노드(Virtual Node)로 불균형 해소
  • Cassandra, DynamoDB, Memcached에서 사용

5.5 리밸런싱 전략

전략설명예시
고정 파티션 수처음부터 파티션 수 고정, 노드에 분배Elasticsearch, Riak
동적 분할파티션 크기 기준으로 분할/병합HBase, RethinkDB
노드 비례노드 수에 비례하여 파티션 수 결정Cassandra

6. 합의 알고리즘 (Consensus)

6.1 합의 문제란?

분산 시스템의 모든 노드가 하나의 값에 합의하는 것. 리더 선출, 원자적 커밋, 분산 락 등에 필수.

6.2 Paxos 기초

Leslie Lamport가 1989년 제안. 이해하기 어렵기로 유명합니다.

세 가지 역할:

  • Proposer: 값을 제안
  • Acceptor: 값을 수락/거부
  • Learner: 합의된 값을 학습

두 단계:

Phase 1: Prepare
  ProposerAcceptor: "제안 번호 n을 준비해주세요"
  AcceptorProposer: "OK" (이전에 수락한 값이 있으면 함께 전달)

Phase 2: Accept
  ProposerAcceptor: "제안 번호 n, 값 v를 수락해주세요"
  AcceptorProposer: "수락" (과반수가 수락하면 합의 완료)
  • 문제점: 라이브락 가능, Multi-Paxos로 해결

6.3 Raft 상세

Diego Ongaro와 John Ousterhout가 2014년 제안. Paxos보다 이해하기 쉽도록 설계.

핵심 개념: 리더 기반 합의

노드 상태: Leader, Follower, Candidate

6.3.1 리더 선출 (Leader Election)

시간 →
           Term 1          Term 2
Follower: [Heartbeat]... [Timeout!]Candidate
Candidate: 자신에게 투표 + 다른 노드에 RequestVote
           과반수 득표 → Leader 당선!

Leader:   [Heartbeat][Heartbeat][Heartbeat]...
# Raft 리더 선출 의사코드
class RaftNode:
    def __init__(self):
        self.state = "follower"
        self.current_term = 0
        self.voted_for = None
        self.election_timeout = random(150, 300)  # ms

    def on_timeout(self):
        self.state = "candidate"
        self.current_term += 1
        self.voted_for = self.id
        votes = 1  # 자기 자신

        for peer in self.peers:
            if peer.request_vote(self.current_term, self.log):
                votes += 1

        if votes > len(self.peers) // 2:
            self.state = "leader"
            self.send_heartbeats()

6.3.2 로그 복제 (Log Replication)

ClientLeader: write(x=5)

Leader Log: [Term1:x=3] [Term1:y=7] [Term2:x=5]
AppendEntries
Follower1: [Term1:x=3] [Term1:y=7] [Term2:x=5]Follower2: [Term1:x=3] [Term1:y=7] [Term2:x=5]Follower3: [Term1:x=3] [Term1:y=7] (네트워크 지연)

과반수(3/5) 복제 완료 → 커밋! → 클라이언트에 응답

6.3.3 안전성 보장 (Safety)

  • Election Safety: 한 Term에 최대 한 명의 리더
  • Leader Append-Only: 리더는 로그를 덮어쓰거나 삭제하지 않음
  • Log Matching: 두 로그가 같은 인덱스에 같은 Term의 엔트리면, 그 이전 엔트리도 모두 동일
  • State Machine Safety: 커밋된 엔트리는 모든 노드에서 같은 순서로 적용

6.4 Raft 사용 사례

시스템용도
etcdKubernetes 클러스터 상태 저장
Consul서비스 디스커버리, KV 저장소
CockroachDB분산 SQL 데이터베이스
TiKVTiDB의 분산 KV 저장소

7. 분산 트랜잭션

7.1 2PC (Two-Phase Commit)

Phase 1: Prepare (투표)
  CoordinatorParticipant A: "커밋 가능?"
  CoordinatorParticipant B: "커밋 가능?"
  ACoordinator: "Yes"
  BCoordinator: "Yes"

Phase 2: Commit (실행)
  CoordinatorA: "커밋!"
  CoordinatorB: "커밋!"

문제점:

  • 코디네이터 단일 장애점 (SPOF)
  • 블로킹: Prepare 후 코디네이터 장애 시 참여자가 무한 대기
  • 성능: 동기 프로토콜이라 느림

7.2 3PC (Three-Phase Commit)

2PC의 블로킹 문제를 해결하기 위해 Pre-Commit 단계를 추가합니다.

Phase 1: CanCommit
Phase 2: PreCommit (타임아웃 설정)
Phase 3: DoCommit
  • 네트워크 분할 시 여전히 불일치 가능
  • 실무에서 거의 사용되지 않음

7.3 Saga 패턴

긴 트랜잭션을 여러 로컬 트랜잭션으로 분할하고, 실패 시 보상 트랜잭션을 실행합니다.

주문 생성 Saga:

T1: 주문 생성 → T2: 결제 처리 → T3: 재고 차감 → T4: 배송 시작
                      ↓ 실패!
C2: 결제 취소 ← C1: 주문 취소

두 가지 구현 방식:

방식설명장단점
Choreography이벤트 기반, 각 서비스가 다음 이벤트 발행단순, 결합도 낮음. 추적 어려움
Orchestration중앙 오케스트레이터가 순서 제어추적 쉬움, 중앙 집중 위험
# Saga Orchestrator 의사코드
class OrderSaga:
    steps = [
        ("create_order", "cancel_order"),
        ("process_payment", "refund_payment"),
        ("update_inventory", "restore_inventory"),
        ("arrange_shipping", "cancel_shipping"),
    ]

    def execute(self):
        completed = []
        for action, compensation in self.steps:
            try:
                getattr(self, action)()
                completed.append(compensation)
            except Exception:
                # 보상 트랜잭션 역순 실행
                for comp in reversed(completed):
                    getattr(self, comp)()
                raise SagaFailed()

8. 분산 시계 (Distributed Clocks)

8.1 물리적 시계의 문제

  • NTP(Network Time Protocol) 동기화 오차: 수 ms ~ 수백 ms
  • 시계 드리프트(Clock Drift): 시간이 지남에 따라 점점 벌어짐
  • 윤초(Leap Second): 가끔 1초가 추가되거나 삭제됨

결론: 물리적 시계만으로는 이벤트 순서를 정확히 판단할 수 없습니다.

8.2 Lamport Clock (논리적 시계)

Leslie Lamport가 1978년 제안. 각 프로세스가 카운터를 유지합니다.

규칙:
1. 이벤트 발생 시 카운터 증가
2. 메시지 전송 시 자신의 카운터를 포함
3. 메시지 수신 시 max(자신 카운터, 받은 카운터) + 1

Process A:  (1) (2) (3)────→ (6)
                       send     recv
Process B:       (1) (2) (4) (5)
                       recv     send
  • 한계: 인과 관계는 알 수 있지만, 두 이벤트가 동시(concurrent)인지는 알 수 없음

8.3 Vector Clock (벡터 시계)

각 노드가 모든 노드의 카운터를 벡터로 유지합니다.

3개 노드 A, B, C

A: [1,0,0][2,0,0][2,0,0] send to B
B: [0,0,0][0,1,0][2,2,0] recv from A[2,3,0] send to C
C: [0,0,0][0,0,1][2,3,2] recv from B

동시성 판단:

  • V1 <= V2: V1의 모든 요소가 V2 이하 — V1이 V2보다 먼저(happens-before)
  • V1과 V2가 비교 불가 — 동시(concurrent) 이벤트
def compare_vector_clocks(vc1, vc2):
    """벡터 시계 비교"""
    less = False
    greater = False
    for a, b in zip(vc1, vc2):
        if a < b:
            less = True
        elif a > b:
            greater = True

    if less and not greater:
        return "BEFORE"      # vc1 < vc2
    elif greater and not less:
        return "AFTER"       # vc1 > vc2
    elif not less and not greater:
        return "EQUAL"
    else:
        return "CONCURRENT"  # 동시 이벤트!

8.4 Hybrid Logical Clock (HLC)

물리적 시계 + 논리적 시계의 조합. CockroachDB, MongoDB에서 사용합니다.

HLC = (물리적 시간, 논리적 카운터)

물리적 시간이 같을 때 논리적 카운터로 순서 결정
NTP 오차 범위 내에서 인과 관계 보장

9. 장애 감지 (Failure Detection)

9.1 Heartbeat 방식

Node A"살아있어?"Node B
Node A"응!"Node B

... 시간 초과 ...

Node A"살아있어?"Node B
Node A  (무응답)Node B
Node B 장애로 판단
  • 단순하지만, 네트워크 지연 시 오탐(False Positive) 발생
  • 타임아웃 설정이 중요: 짧으면 오탐, 길면 감지 지연

9.2 Phi Accrual Failure Detector

하트비트 도착 시간의 분포를 학습하여 장애 확률을 계산합니다.

Phi(φ):
  φ = 110% 확률로 장애
  φ = 21% 확률로 장애
  φ = 30.1% 확률로 장애

임계값(: φ > 8)을 넘으면 장애로 판단
  • Cassandra, Akka에서 사용
  • 네트워크 상태에 적응적으로 반응

9.3 SWIM Protocol

Scalable Weakly-consistent Infection-style Membership. 효율적인 그룹 멤버십 프로토콜입니다.

1. Node ANode B에 ping
2. 응답 없으면 → Node C, D"B에 indirect ping 해줘" 요청
3. C, DB에 ping → 응답 없으면 B의심(suspect)
4. 일정 시간 후 여전히 응답 없으면 B를 제거
5. 가십(gossip)으로 멤버십 변경을 전파
  • O(log N) 라운드에 모든 노드에 전파
  • HashiCorp Memberlist, Consul에서 사용

10. 실제 시스템 분석

10.1 Apache Cassandra (AP 시스템)

아키텍처:
  - Leaderless, Consistent Hashing
  - Quorum 읽기/쓰기 (W + R > N)
  - 가십 프로토콜로 멤버십 관리
  - Last-Write-Wins 충돌 해결
  - SSTable + Memtable 저장 구조

PACELC: PA/EL
  - 분할 시: 가용성 우선
  - 정상 시: 낮은 지연 우선

10.2 Google Spanner (CP 시스템)

아키텍처:
  - 전 세계 분산, 강한 일관성
  - TrueTime API: GPS + 원자시계로 시간 불확실성 구간 제공
  - Paxos 기반 복제
  - 외부 일관성(External Consistency) 보장

TrueTime:
  TT.now()[earliest, latest]
  불확실성 구간이 보통 1-7ms

  트랜잭션 커밋 시:
  1. 타임스탬프 s 할당
  2. commit-wait: TT.after(s)true가 될 때까지 대기
  3. 불확실성 구간만큼 대기하여 순서 보장

10.3 Amazon DynamoDB (AP 시스템)

아키텍처:
  - Consistent Hashing + 가상 노드
  - Sloppy Quorum: 장애 시 다른 노드가 대신 응답
  - Hinted Handoff: 복구 후 원래 노드에 데이터 전달
  - Vector Clock으로 버전 관리
  - Merkle Tree로 안티 엔트로피

10.4 시스템 비교표

특성CassandraSpannerDynamoDB
CAP 분류APCPAP
일관성TunableStrongTunable
복제LeaderlessPaxosLeaderless
파티셔닝Consistent HashRangeConsistent Hash
시계NTPTrueTimeVector Clock
쿼리 언어CQLSQLPartiQL

11. DDIA 핵심 요약

Martin Kleppmann의 "Designing Data-Intensive Applications" 핵심 내용:

Part 1: 데이터 시스템의 기초

핵심 키워드
1장신뢰성, 확장성, 유지보수성
2장관계형 vs 문서 vs 그래프 모델
3장저장 엔진: B-Tree vs LSM-Tree
4장인코딩: JSON, Protobuf, Avro

Part 2: 분산 데이터

핵심 키워드
5장복제: Leader-Follower, Leaderless
6장파티셔닝: Hash, Range
7장트랜잭션: ACID, 격리 수준
8장분산 시스템의 어려움
9장일관성과 합의

Part 3: 파생 데이터

핵심 키워드
10장배치 처리: MapReduce
11장스트림 처리: 이벤트 소싱
12장데이터 시스템의 미래

12. 면접 질문 15선

Q1. CAP 정리를 설명하고, 실제 시스템에서 어떻게 적용되는지 예를 들어주세요.

CAP 정리는 분산 시스템에서 일관성(Consistency), 가용성(Availability), 분할내성(Partition Tolerance) 중 최대 두 가지만 동시에 보장할 수 있다는 정리입니다. 네트워크 분할은 피할 수 없으므로 실질적으로 CP(일관성 우선)와 AP(가용성 우선) 중 선택합니다. ZooKeeper는 CP 시스템으로 리더 선출과 분산 락에 적합하고, Cassandra는 AP 시스템으로 높은 쓰기 처리량이 필요한 경우에 적합합니다.

Q2. Raft 합의 알고리즘의 리더 선출 과정을 설명하세요.

Raft에서 팔로워가 election timeout 내에 리더의 하트비트를 받지 못하면 후보(Candidate)가 됩니다. 후보는 Term을 증가시키고, 자신에게 투표한 뒤, 다른 노드에 RequestVote RPC를 보냅니다. 과반수의 투표를 받으면 리더가 되고, 즉시 하트비트를 보내 자신의 리더십을 알립니다. 각 Term에 최대 한 명의 리더만 존재할 수 있습니다.

Q3. Eventual Consistency와 Strong Consistency의 차이를 설명하세요.

Strong Consistency는 모든 읽기가 가장 최근 쓰기의 결과를 반환합니다. 동기 복제가 필요하여 지연이 높고 가용성이 낮습니다. Eventual Consistency는 업데이트 중단 후 최종적으로 모든 복제본이 동일한 값을 갖게 됩니다. 중간에 오래된 데이터를 읽을 수 있지만 지연이 낮고 가용성이 높습니다. DNS, 캐시 시스템에서 주로 사용됩니다.

Q4. Consistent Hashing을 설명하고, 가상 노드가 왜 필요한지 말해주세요.

Consistent Hashing은 해시 공간을 원(Ring)으로 구성하여 노드 추가/제거 시 최소한의 키만 재배치하는 방식입니다. 키와 노드를 같은 해시 함수로 매핑하고, 키는 시계 방향으로 가장 가까운 노드에 할당됩니다. 가상 노드(Virtual Node)는 물리 노드가 여러 해시 위치를 가지게 하여 데이터 분포의 불균형을 해소합니다.

Q5. 2PC와 Saga 패턴의 차이를 설명하세요.

2PC(Two-Phase Commit)는 코디네이터가 모든 참여자에게 prepare/commit을 지시하여 원자적 커밋을 보장합니다. 동기적이고 블로킹이며 코디네이터가 SPOF입니다. Saga 패턴은 긴 트랜잭션을 로컬 트랜잭션 체인으로 분할하고, 실패 시 보상 트랜잭션을 역순 실행합니다. 비동기적이고 확장성이 좋지만 격리성이 보장되지 않습니다.

Q6. Vector Clock이 Lamport Clock보다 나은 점은 무엇인가요?

Lamport Clock은 이벤트 순서(happens-before)를 판단할 수 있지만, 두 이벤트가 동시(concurrent)인지 알 수 없습니다. Vector Clock은 각 노드가 모든 노드의 카운터를 벡터로 유지하여 동시성을 정확히 판단할 수 있습니다. 벡터 비교가 불가능하면 두 이벤트는 동시적이며, 충돌 해결이 필요합니다.

Q7. Leader-Follower, Multi-Leader, Leaderless 복제의 장단점을 비교하세요.

Leader-Follower는 단일 쓰기 지점으로 충돌이 없지만 리더가 SPOF이고 쓰기 확장이 어렵습니다. Multi-Leader는 다중 데이터센터에서 쓰기 지연이 낮지만 충돌 해결이 복잡합니다. Leaderless는 높은 가용성과 쓰기 처리량을 제공하지만 Quorum 설정과 충돌 해결이 필요합니다.

Q8. PACELC 모델이 CAP을 어떻게 확장하나요?

PACELC는 네트워크 분할(P) 시 가용성(A) vs 일관성(C) 선택에 더해, 정상(E: else) 상태에서 지연(L) vs 일관성(C) 트레이드오프를 추가합니다. Cassandra는 PA/EL(분할 시 가용성, 정상 시 낮은 지연)이고, Spanner는 PC/EC(항상 일관성)입니다.

Q9. Google Spanner의 TrueTime이 강한 일관성을 어떻게 보장하나요?

TrueTime은 GPS와 원자시계를 사용해 시간 불확실성 구간을 제공합니다. 트랜잭션 커밋 시 타임스탬프를 할당하고, commit-wait로 불확실성 구간만큼 대기합니다. 이렇게 하면 나중에 커밋된 트랜잭션이 반드시 더 큰 타임스탬프를 가지게 되어, 전 세계적으로 외부 일관성이 보장됩니다.

Q10. Quorum 읽기/쓰기에서 W + R > N이 왜 일관성을 보장하나요?

N개 노드에서 W개에 쓰고 R개에서 읽으면, W + R > N일 때 쓰기 집합과 읽기 집합에 반드시 교집합이 있습니다. 이 교집합 노드가 최신 데이터를 가지고 있으므로 읽기 시 최신 값을 반환할 수 있습니다. 예를 들어 N=3, W=2, R=2이면 최소 1개 노드가 공통됩니다.

Q11. 분산 시스템에서 Split-Brain 문제를 어떻게 해결하나요?

Split-Brain은 네트워크 분할로 두 파티션이 각각 자신이 리더라고 판단하는 상황입니다. 해결 방법: Quorum 기반 리더 선출(과반수 없는 파티션은 리더를 선출할 수 없음), Fencing Token(이전 리더의 요청을 거부), STONITH(Shoot The Other Node In The Head, 의심스러운 노드를 강제 종료) 등이 있습니다.

Q12. LSM-Tree와 B-Tree의 차이를 분산 시스템 관점에서 설명하세요.

LSM-Tree는 쓰기 최적화 구조로 모든 쓰기가 순차적이며, Compaction으로 정리합니다. 쓰기 집중 워크로드(Cassandra, HBase)에 적합합니다. B-Tree는 읽기 최적화 구조로 제자리 업데이트(in-place update)를 합니다. 읽기 집중 워크로드(전통 RDBMS)에 적합합니다. 분산 환경에서 LSM-Tree는 복제 지연이 적고 높은 쓰기 처리량을 제공합니다.

Q13. Phi Accrual Failure Detector가 단순 타임아웃보다 좋은 이유는?

단순 타임아웃은 고정된 시간 내에 응답이 없으면 장애로 판단합니다. 네트워크 지연이 변동하면 오탐이 많습니다. Phi Accrual Failure Detector는 하트비트 도착 시간의 통계적 분포를 학습하여, 현재 도착 시간이 분포에서 얼마나 이탈했는지(phi 값)로 장애 확률을 계산합니다. 네트워크 상태 변화에 적응적입니다.

Q14. Saga 패턴에서 Choreography와 Orchestration의 차이는?

Choreography는 각 서비스가 이벤트를 발행하고 다른 서비스가 구독하여 다음 단계를 수행합니다. 중앙 제어가 없어 결합도가 낮지만 흐름 추적이 어렵습니다. Orchestration은 중앙 오케스트레이터가 전체 흐름을 제어합니다. 추적과 디버깅이 쉽지만 오케스트레이터가 SPOF가 될 수 있습니다.

Q15. 분산 시스템에서 멱등성(Idempotency)이 왜 중요한가요?

네트워크 장애로 요청이 재전송될 수 있으므로, 같은 요청을 여러 번 수행해도 결과가 동일해야 합니다. 멱등키(Idempotency Key)를 사용하여 중복 요청을 감지하고, 결제나 재고 차감 같은 부작용 있는 연산에서 중복 처리를 방지합니다.


13. 퀴즈 5선

퀴즈 1. N=5인 분산 시스템에서 W=3, R=2일 때 일관된 읽기가 보장되는가?

보장됩니다. W + R = 3 + 2 = 5 > N(5)이므로 쓰기 집합과 읽기 집합이 반드시 겹칩니다. 단, W + R = N인 경우는 경계값이므로 모든 노드가 정상일 때만 보장됩니다.

퀴즈 2. Raft에서 Term 5의 리더가 있을 때, Term 3의 RequestVote를 받으면 어떻게 하나요?

거부합니다. Raft에서 노드는 자신의 현재 Term보다 작은 Term의 요청을 항상 거부합니다. 이를 통해 오래된 리더의 간섭을 방지합니다.

퀴즈 3. Vector Clock V1=[2,3,1]과 V2=[3,2,1]의 관계는?

동시(Concurrent) 이벤트입니다. V1의 첫 번째 요소(2)가 V2(3)보다 작지만, 두 번째 요소(3)가 V2(2)보다 큽니다. 어느 한쪽이 완전히 크거나 같지 않으므로 비교 불가능하며, 이는 동시 이벤트를 의미합니다.

퀴즈 4. Cassandra에서 ONE, QUORUM, ALL 일관성 수준의 차이는?
  • ONE: 1개 노드 응답으로 충분. 가장 빠르지만 오래된 데이터를 읽을 수 있음
  • QUORUM: 과반수(N/2+1) 노드 응답 필요. 균형 잡힌 선택
  • ALL: 모든 노드 응답 필요. 가장 느리지만 강한 일관성. 하나라도 장애면 실패
퀴즈 5. 2PC에서 Prepare 후 코디네이터가 장애 나면 어떻게 되나요?

참여자들이 블로킹 됩니다. Prepare에 Yes로 응답한 참여자는 커밋도 중단도 할 수 없어 리소스 잠금 상태로 무한 대기합니다. 이것이 2PC의 가장 큰 문제점이며, 3PC나 코디네이터 로그 복구로 해결합니다.


14. 참고 자료

도서

  1. Martin Kleppmann, "Designing Data-Intensive Applications" (2017)
  2. Maarten van Steen, Andrew S. Tanenbaum, "Distributed Systems" (4th ed., 2023)
  3. Mikito Takada, "Distributed Systems for Fun and Profit" (온라인 무료)

논문

  1. Brewer, E., "CAP Twelve Years Later" (IEEE Computer, 2012)
  2. Lamport, L., "The Part-Time Parliament" (1998) - Paxos 원본 논문
  3. Ongaro, D. & Ousterhout, J., "In Search of an Understandable Consensus Algorithm" (2014) - Raft 논문
  4. Lamport, L., "Time, Clocks, and the Ordering of Events in a Distributed System" (1978)
  5. DeCandia, G. et al., "Dynamo: Amazon's Highly Available Key-value Store" (2007)
  6. Corbett, J.C. et al., "Spanner: Google's Globally-Distributed Database" (2012)

웹 자료

  1. Jepsen - 분산 시스템 안전성 테스트: https://jepsen.io/
  2. The Raft Consensus Algorithm 시각화: https://raft.github.io/
  3. Aphyr의 분산 시스템 강의: https://github.com/aphyr/distsys-class
  4. AWS re:Invent 분산 시스템 발표 영상 시리즈
  5. Martin Kleppmann 블로그: https://martin.kleppmann.com/
  6. Marc Brooker 블로그: https://brooker.co.za/blog/
  7. Distributed Systems Reading Group: http://dsrg.pdos.csail.mit.edu/