백엔드 엔지니어링수정 2026-08-18

데이터베이스 확장 Q&A — 트레이드오프 16선과 32문답

선택지 열여섯 개가 각각 무엇을 얻고 무엇을 내주는지, 실패 모드 열한 가지가 어떤 증상으로 드러나고 무엇으로 되받는지를 표로 모았다. 파티셔닝과 샤딩, 복제, 분산 시스템, 합의에 걸친 서른두 개 문답은 먼저 답을 내고 근거를 뒤에 붙인다.

확장을 다루는 일이 어려운 이유는 개별 기법을 몰라서가 아니다. 파티셔닝도 알고 복제도 아는 상태에서, 이 서비스가 지금 무엇을 내주고 무엇을 받을지 정해야 하기 때문이다. 손실을 0으로 둘 것인가 응답을 빠르게 할 것인가, 샤딩을 지금 넣을 것인가 더 버틸 것인가 — 답이 하나로 고정되지 않는 자리들이다.

이 글은 앞 다섯 편이 세운 선택지들을 선택지 표 · 실패 모드 표 · 문답 세 형태로 다시 세운다. 확장 방식을 지금 결정해야 하는 상황, 그리고 이미 운영 중인 시스템에서 출처가 잡히지 않는 지연·불일치·데이터 소실을 추적하는 상황에서 쓸모가 있다.

그 편이 세운 축이 글에서 받는 묶음
1. 파티셔닝의 출발점왜 쪼개고 어느 방향으로 쪼개는가. 이 시리즈의 용어 32개가 그 글에 모여 있다용어 정의 전부
2. 파티셔닝 전략과 샤딩무엇을 기준으로 자를 것인가, 인덱스도 함께 자를 것인가, 조각을 다른 머신에 보낼 것인가나누기 — 8문답
3. 복제몇 벌을 어떤 모양으로 두고, 쓰기가 어디까지 닿기를 기다릴 것인가복사하기 — 9문답
4. 분산 시스템과 CAP네트워크가 갈라졌을 때 무엇이 원리적으로 불가능하고 무엇을 포기하는가갈라짐 — 5문답
5. 분산 트랜잭션과 합의흩어진 노드를 하나의 결정으로 모으는 방법과 그 값합의 — 6문답

오른쪽 열의 숫자를 더하면 28이고, 한 줄로 답할 수 없어 뒤에 따로 뺀 네 문항을 더하면 32다. 주제별로 묶고 개수를 배정한 것은 이 글의 편집이다 — 바탕이 된 자료는 번호만 차례로 매긴 한 장의 표였다.

선택지 16개 — 무엇을 얻고 무엇을 내주는가

선택지얻는 것내주는 것언제 쓰나
Range 파티셔닝기간 단위 관리가 최적화되고, 지난 구간은 파티션 DROP으로 지운다최근 구간 파티션에 접근이 몰려 핫스팟이 생긴다보존 정책이 걸린 이력성 데이터
Hash 파티셔닝균등 분산범위 조회가 성립하지 않고, 파티션 수를 바꾸면 대규모 재배치균등 분산 하나만이 목적일 때
Composite기간 관리와 경합 분산을 동시에설계·운영 복잡도가 오른다대량 이력에 높은 동시 쓰기가 겹칠 때
로컬 인덱스파티션을 DROP해도 인덱스가 무효화되지 않는다파티션 키 없는 조회는 전 파티션을 훑는다파티션 키가 조건에 항상 들어올 때
글로벌·비파티션 인덱스파티션 키 없이 던지는 조회가 빠르다파티션 DDL 한 번에 인덱스가 전역 무효화된다파티션 키 없이 조회하는 OLTP
샤딩진짜 Scale-Out되돌릴 수 없다. 크로스 샤드 조인과 트랜잭션이 불가능해진다단일 노드의 한계를 이미 넘은 뒤
단일 리더 복제쓰기 충돌이 없고 구조가 단순하다리더가 죽으면 페일오버가 필요하고, 쓰기는 확장되지 않는다대부분의 OLTP
다중 리더 복제지역별로 가까운 곳에 쓰고, 네트워크 장애를 견딘다쓰기 충돌 해소가 어렵다멀티 DC, 오프라인 편집
리더리스 복제페일오버 절차 자체가 없어 가용성이 가장 높다쿼럼을 튜닝해야 하고 충돌이 발생한다초고가용성이 요구될 때
동기 복제데이터 손실이 0팔로워 한 대가 느리면 전체가 막힌다금전·재고처럼 손실이 허용되지 않는 데이터
반동기 복제손실과 지연 사이의 균형타임아웃이 걸리면 비동기로 강등되어 손실 위험이 되살아난다실무 기본값
비동기 복제쓰기 성능이 가장 높다페일오버 시 아직 전달되지 않은 쓰기가 사라진다손실을 감당할 수 있는 데이터
2PC원자성 보장블로킹, 코디네이터라는 단일 장애점, 디스크 로그 비용단일 조직 안에서 짧게 끝나는 분산 트랜잭션
Saga블로킹이 없고 MSA에 맞는다격리성이 없고 보상 로직을 직접 써야 한다MSA, 오래 걸리는 비즈니스 프로세스
Raft·Paxos리더 유일성과 로그 일관성을 보장과반 노드가 필요하고 쓰기마다 합의 지연이 붙는다메타데이터·설정 관리, 리더 선출
PBFT악의적 노드까지 견딘다메시지가 O(N²)이고 노드가 3f+1 필요참가자를 신뢰할 수 없을 때

열여섯 행을 확장의 축으로 갈라 보면 나누기 6행 · 복사하기 6행 · 합의 4행이다. 앞 여섯이 파티셔닝·인덱스·샤딩, 가운데 여섯이 토폴로지 셋과 동기화 강도 셋, 뒤 넷이 분산 트랜잭션 둘과 합의 알고리즘 둘이다. 이 6·6·4 배정은 이 글이 매긴 것이다.

「내주는 것」 열을 세로로 읽으면 한 행만 성격이 다르다. 나머지 열다섯 행이 내주는 것은 성능이거나 복잡도이거나 운영 부담이어서, 나중에 값을 더 치르면 되돌릴 수 있는 종류다. 샤딩 한 행만 되돌림 자체가 불가능하다는 것을 대가로 적는다. 뒤에 따로 뺀 네 문항 중 하나가 그 행의 도입 시점을 다룬다.

실패 모드 11가지 — 증상에서 대응으로

실패 모드증상대응
Pruning 미적용파티셔닝을 넣었는데 오히려 느려진다실행계획으로 접근한 파티션 수를 확인. 파티션 키를 쿼리 조건에 넣거나 전략을 다시 짠다
파티션 스큐특정 파티션만 비대해진다파티션 키를 다시 고르거나, Composite로 하위에 Hash를 덧붙인다
Jumbo Chunk청크가 더 이상 분할되지 않는다카디널리티가 높은 샤드 키로 리샤딩, 또는 해시 샤딩
Hot Shard최신 데이터가 한 샤드로 몰린다해시 샤딩, 또는 해시 prefix에 시간을 붙인 복합 샤드 키
크로스 샤드 브로드캐스트시간이 갈수록 느려진다쿼리에 샤드 키를 싣는다. 불가능하면 조회 전용 인덱스 저장소를 따로 둔다
복제 지연방금 쓴 데이터가 조회되지 않는다쓰기 직후에는 리더에서 읽기, 세션 고정, 지연 모니터링 알람
페일오버 데이터 손실성공 응답을 받은 데이터가 사라진다반동기 복제. 손실이 허용되지 않는 데이터는 동기
스플릿 브레인리더가 둘이 되고 데이터가 갈라진다홀수 노드 + 과반 쿼럼, fencing token
선출 폭풍페일오버가 반복된다타임아웃 상향, 연속 실패 횟수 조건, 다중 관측자의 동의
2PC In-doubt락을 쥔 채 무한 대기한다코디네이터 이중화. 타임아웃 뒤 휴리스틱 결정 + 감사 로그
Saga 보상 실패부분 성공 상태가 그대로 남는다보상을 멱등하게 구현. 실패 시 재시도 큐 + 수동 개입 대시보드

「대응」 열을 기준으로 열한 행을 가르면 앞 네 행과 뒤 일곱 행으로 갈린다. 앞 네 행의 대응에는 「키를 다시 고른다」·「전략을 다시 짠다」·「리샤딩한다」가 들어간다. 이미 확정된 설계를 되돌리는 작업이고, 셋째·넷째 행에서는 그 되돌림이 곧 리샤딩이다. 선택지 표에서 샤딩만 되돌릴 수 없다고 적혔던 것이 여기서 실제 청구서로 돌아온다.

뒤 일곱 행의 대응은 이미 자른 것을 다시 자르지 않는다. 설정을 바꾸거나, 경로·복제본을 하나 더 두거나, 보정 장치를 덧대는 선에서 끝난다. 4와 7로 가른 이 배정은 이 글이 매긴 것이다.

나누기 — 파티셔닝과 샤딩 8문답

질문결론
파티셔닝과 샤딩은 무엇이 다른가머신 경계를 넘느냐가 갈림길이다. 파티셔닝은 한 인스턴스 안의 분할이라 로컬 트랜잭션과 조인이 그대로 남고, 샤딩은 여러 서버로 흩어 Scale-Out을 얻는 대신 그 둘을 잃는다. 그리고 샤딩은 되돌릴 수 없다
파티셔닝을 했는데 빨라지지 않는다1순위 원인은 Pruning 미적용이다. 조건에 파티션 키가 없으면 전 파티션 스캔에 분할 오버헤드까지 얹힌다. 실행계획에서 접근한 파티션 수부터 본다. 2순위는 데이터 스큐, 3순위는 인덱스 전략 불일치다
로컬 인덱스와 글로벌 인덱스는 언제 각각 쓰나파티션 키가 조건에 항상 들어오면 로컬(Prefixed)이다. 파티션 키 없이 다른 컬럼으로 찾는다면 비파티션·글로벌 쪽인데, 파티션 DROP 운영이 잦다면 전역 무효화 비용을 먼저 계산한다
샤드 키는 무엇을 보고 고르나카디널리티·데이터 분포·쿼리 패턴 셋이고, 하나씩 따로가 아니라 동시에 봐야 한다. 실무 해법은 해시 prefix에 범위 컬럼을 붙인 복합 샤드 키다
주문ID를 샤드 키로 쓰면 어떻게 되나serial 증가라 최신 문서가 한 샤드·한 청크로 몰려 Hot Shard와 Hot Chunk가 된다. 해시 샤딩이나 리샤딩으로 푸는데, 리샤딩은 전체 재분배라 서비스에 영향이 간다
Range 샤딩과 Hash 샤딩은 리밸런싱 비용이 어떻게 다른가Range는 경계만 조정하면 되어 이동량이 적다. 모듈러 Hash는 N이 바뀌면 대부분이 재배치된다. Consistent Hash는 이동량이 평균 1/N 수준에 머문다
Kafka 파티션 수를 늘리면 무엇이 좋아지고 무엇이 깨지나병렬 소비와 처리량이 오른다. 대신 hash(key) % N 의 결과가 바뀌어 키 단위 순서 보장이 그 지점에서 끊긴다. 그리고 파티션 수는 줄일 수 없다
Kafka에서 전역 순서를 보장하려면파티션을 1개로 두는 방법밖에 없고, 그러면 병렬성을 통째로 포기한다. 통상은 키 단위 순서로 충분하도록 도메인을 설계하는 쪽을 답으로 본다

여덟 문항은 1·2·5로 갈린다. 첫 문항이 경계 자체를 정의하고, 둘째와 셋째가 같은 인스턴스 안에서 끝나는 문제이며, 나머지 다섯이 경계를 넘은 뒤의 문제다. 경계를 넘는 순간 질문의 성격이 「어떻게 빠르게 할까」에서 「무엇을 포기하고 시작할까」로 바뀐다는 것이 첫 문항의 답이 말하는 바다. 분할 전략을 고르는 기준과 인덱스를 함께 자를지의 판단, 그리고 파티셔닝이 샤딩으로 넘어가는 경계는 파티셔닝 전략과 샤딩에 있다.

복사하기 — 복제 9문답

질문결론
복제는 무엇 때문에 하나셋이다. 장애가 나도 계속 도는 고가용성, 읽기 처리량을 나누는 부하 분산, 사용자 가까이 두어 지연을 줄이는 지역 근접성
동기·반동기·비동기의 트레이드오프는동기는 손실이 0이지만 느린 팔로워 한 대가 전체 쓰기를 세운다. 비동기는 빠른 대신 페일오버 때 잃는다. 반동기가 실무 기본값이나 타임아웃이 걸리면 비동기로 자동 강등되어 손실 위험이 그대로 되살아난다
복제 지연이 만드는 이상 현상과 해결은RYW 위반은 쓰기 직후 리더에서 읽어 막고, Monotonic Read 위반은 같은 팔로워에 고정해 막으며, Consistent Prefix 위반은 인과 관계가 있는 쓰기를 같은 파티션으로 몰아 막는다
복제 로그 4가지 방식은 각각 무엇이 문제인가구문 기반은 비결정적 함수에서 어긋나고, WAL은 엔진 포맷에 묶여 롤링 업그레이드가 어렵다. Row 기반(CDC)은 로그가 커지는 대신 외부 연동이 열리고, 트리거 기반은 오버헤드와 버그를 안는다
리더가 죽으면 어떤 절차를 밟고 무엇이 위험한가장애 감지 → 새 리더 선출 → 라우팅 재설정이다. 비동기였다면 아직 복제되지 않은 쓰기가 사라지고, 구 리더가 살아 돌아오면 데이터가 갈라진다. 통상 구 리더의 미복제 쓰기를 버리는 쪽으로 정리한다
스플릿 브레인은 왜 생기고 어떻게 막나네트워크 분할로 리더가 둘이 되는 상태다. 과반 쿼럼(그래서 홀수 노드), fencing token, STONITH로 막는다. 노드 수가 짝수여서 정확히 반으로 갈리면 양쪽 모두 과반을 얻지 못해 어느 쪽도 리더가 되지 못한다
단일·다중·리더리스는 각각 어디에 맞나단일 리더는 충돌이 없어 대부분의 OLTP에, 다중 리더는 충돌 해소를 감수하는 멀티 DC·오프라인 편집에, 리더리스는 페일오버가 필요 없는 초고가용성 요구에 맞는다. 리더리스에는 쿼럼 튜닝이 따라온다
쿼럼 w + r > n 은 왜 최신값을 보장하나쓰기가 성공한 노드 집합과 읽기를 질의한 집합의 크기 합이 n을 넘으면 두 집합은 최소 한 노드에서 반드시 겹치고, 겹친 그 노드가 최신값을 갖고 있기 때문이다
Read Repair와 Anti-Entropy는 어떻게 다른가Read Repair는 읽는 순간 고치므로 자주 읽히는 데이터만 복구된다. Anti-Entropy는 백그라운드로 주기 비교를 돌려 잘 안 읽히는 데이터까지 닿지만 복구가 늦다. 그래서 둘을 함께 쓴다

둘째 문항의 답에서 뒤집히는 조건 하나가 이 묶음 전체의 요점이다. 반동기를 켜 두었다는 사실이 손실 0을 뜻하지 않는다 — 강등이 일어난 그 순간의 쓰기는 비동기와 똑같은 위험을 진다. 「반동기로 설정했다」와 「지금 반동기로 동작하고 있다」가 다른 문장이라는 뜻이고, 그래서 여섯째 실패 모드의 대응에 지연 모니터링 알람이 들어간다. 토폴로지 셋의 구조와 지연이 만드는 이상 현상은 복제에 있다.

갈라짐 — 분산 시스템 5문답

질문결론
CAP를 「셋 중 둘」로 설명하면 무엇이 틀리나P는 고르는 항목이 아니라 반드시 일어나는 사실이다. 실제 선택지는 분할이 났을 때 C를 잡을지 A를 잡을지 하나뿐이고, CA를 골랐다는 말은 분산 시스템이 아니라는 말과 같다
PACELC는 CAP에 무엇을 더하나분할이 없는 정상 상태(Else)에서도 지연(L)과 일관성(C) 사이에 선택이 있다는 것이다. 분할은 드물지만 지연은 매 요청 발생하므로 실무에서 더 큰 비용은 EL/EC 쪽에서 나온다
FLP 불가능성 정리는 실무에 무엇을 남기나비동기 시스템에서는 「죽음」과 「느림」을 구별할 수 없어, 반드시 종료하는 합의 프로토콜이 존재하지 않는다. 그래서 Raft는 종료 보장을 포기하고 랜덤 타임아웃으로 확률적 종료를 택했다
장애 감지에서 SWIM이 하트비트보다 나은 점은직접 프로빙이 실패했을 때 다른 노드에 간접 프로빙을 부탁해, 일시적 네트워크 문제로 인한 오탐을 크게 줄인다. 중앙 모니터 한 대에 걸리던 O(N) 부하도 흩어진다
내결함성 시스템의 4원칙은복제·중복, 점진적 성능 저하, 결함 격리(방화문), 결함 감지다. 이 중 격리가 단일 지점 장애를 없애는 핵심이다

이 다섯은 앞의 두 묶음과 층이 다르다. 나누기와 복사하기가 「어떻게 할 것인가」를 묻는다면, 여기는 먼저 무엇이 원리적으로 불가능한가를 확정하고, 그 불가능성을 인정한 채 무엇을 덧대어 버틸 것인가를 잇는다. 앞의 세 문항이 앞쪽을, 뒤의 두 문항이 뒤쪽을 맡는다. 분할은 반드시 일어나고 죽음은 느림과 구별되지 않는다 — 이 두 사실이 다음 묶음의 프로토콜들이 왜 그런 모양인지를 설명한다. 장애 모델과 감지·선출, 그리고 CAP와 PACELC의 전체 맥락은 분산 시스템과 CAP에 있다.

합의 — 분산 트랜잭션과 합의 6문답

질문결론
2PC의 가장 큰 문제는1단계에서 Yes를 보낸 뒤 코디네이터가 죽으면 코호트는 Commit도 Abort도 못 하고 락을 쥔 채 무한 블로킹에 빠진다. 코디네이터 이중화나 복구 알고리즘이 선택이 아니라 필수인 이유다
3PC는 2PC의 무엇을 고쳤나준비 단계에서 코호트끼리 상태를 공유해, 코디네이터가 죽어도 코호트가 스스로 커밋·중단을 결정할 수 있게 했다. 블로킹이 완화되는 대신 메시지 라운드가 늘어난다
MSA에서 2PC 대신 무엇을 쓰나Saga다. 로컬 트랜잭션을 연쇄시키고 실패하면 보상 트랜잭션을 돌린다. 격리성을 내주고 블로킹을 없애는 교환이며, 보상이 불가능한 단계(메일 발송·외부 결제 승인)는 맨 뒤에 배치한다
Raft는 리더가 하나뿐임을 어떻게 보장하나과반 득표를 요구하고 term마다 표를 한 장만 준다. 과반을 이루는 두 집합은 동시에 존재할 수 없다. 여기에 Request Vote가 최종 로그 ID를 싣고 다녀, 로그가 뒤처진 후보는 표를 받지 못한다
Raft와 PBFT는 무엇이 다른가전제하는 장애 모델이 다르다. Raft는 Crash 내성(2f+1 노드, O(N) 메시지), PBFT는 Byzantine 내성(3f+1 노드, O(N²) 메시지)이다. 참가자를 신뢰할 수 있다면 Raft로 충분하다
블록체인을 도입하지 말아야 할 때는기존 DB로 풀리거나, 공유 쓰기가 필요 없거나, 참여자를 전부 신뢰할 수 있거나, 제3자 개입이 오히려 필수인 경우다. 「신뢰할 수 없는 다수가 같은 데이터에 쓰는가」가 아니면 과잉이다

여섯 문항이 두 종류로 갈린다. 앞 셋은 여러 자원에 걸친 하나의 작업을 어떻게 끝낼 것인가의 문제이고, 뒤 셋은 여러 노드가 하나의 값에 어떻게 동의할 것인가의 문제다. 둘은 자주 뭉뚱그려지지만 실패했을 때 남는 것이 다르다 — 앞쪽이 실패하면 데이터가 어중간한 상태로 남고, 뒤쪽이 실패하면 시스템이 결정을 내리지 못한 채 멈춘다. 2PC의 구조와 Saga, 합의 알고리즘들의 비교는 분산 트랜잭션과 합의에 있다.

Q. 방금 쓴 글이 안 보인다는 제보가 올라왔다

이것을 버그가 아니라 구조의 알려진 성질로 규정하는 데서 시작한다. 읽기 부하를 나누려고 팔로워에서 읽게 만든 순간, 비동기 복제 위에서 Read-Your-Writes 위반은 예외가 아니라 정상 동작이다. 원인을 애플리케이션 코드에서 찾기 시작하면 고칠 자리를 영영 못 찾는다.

그다음은 범위를 좁히는 일이다. 모든 읽기를 리더로 되돌리면 부하 분산이 통째로 사라지므로, 쓰기 직후 일정 시간 동안 그 사용자의 읽기만 리더로 보낸다. 세션에 마지막 쓰기 시각을 남기고, 그 시점부터 정해진 구간 안이면 리더로 라우팅하는 방식이다.

처방무엇을 보장하나대가
모든 읽기를 리더로세 가지 이상 현상을 전부 막는다읽기 부하 분산이 사라진다
쓰기 직후 구간만 리더로그 사용자의 RYW세션 상태와 시간 임계치를 관리해야 한다
사용자ID 해시로 팔로워 고정Monotonic Read. RYW는 보장하지 않는다고정된 팔로워가 뒤처지면 그 사용자만 계속 늦게 본다
지연이 임계치 이내인 팔로워만 후보로지연의 상한지연을 상시 조회해야 하고, 후보가 0이 되는 순간의 동작을 정의해야 한다

셋째 행은 둘째 행의 대체재가 아니다. 팔로워를 고정해도 그 팔로워가 자기 쓰기를 아직 못 받았을 수 있으므로 RYW는 여전히 깨진다. 팔로워 라우팅을 유지해야 할 때 최소한을 건지는 선택이지, 같은 문제를 푸는 다른 방법이 아니다.

마지막으로 지표를 건다. 복제 지연에 알람이 없으면 같은 문제가 다음번에는 더 큰 규모로 돌아온다. 근본 대책은 라우팅 코드가 아니라 지연을 관측 가능한 값으로 만들어 두는 쪽에 있다.

Q. 주문과 재고가 각각 DB를 가진 MSA에서 정합성을 어떻게 세우나

2PC는 후보에서 뺀다. 코디네이터가 1단계 뒤에 죽으면 두 서비스가 락을 쥔 채 무한히 기다리고, 이 블로킹은 한 트랜잭션의 실패로 끝나지 않는다. 대기가 쌓이면서 서비스 전체의 가용성 사고로 번진다.

남는 선택은 Saga다. 「주문 생성(PENDING) → 재고 차감 → 주문 확정」 순서로 로컬 트랜잭션을 잇고, 재고 차감이 실패하면 주문 취소 보상 트랜잭션을 돌린다.

도식을 탭하면 확대해서 볼 수 있습니다

도식에서 한 줄만 정상 흐름이고 나머지 갈래는 전부 실패 경로다.

정해야 할 것무엇으로 정하나안 정하면
조율 방식단계가 셋 이하면 이벤트 기반 Choreography, 그보다 많거나 보상 로직이 복잡하면 Orchestration. 앞 편의 표는 이 경계를 3~4개 이하로 적는다단계가 늘수록 전체 흐름을 따라갈 수 없게 된다
중간 상태의 표현PENDING이 외부에 보인다는 사실을 UI와 정책이 받는다. 「결제 대기 중」이 그 표현이다격리성이 없다는 대가가 사용자에게 버그로 보인다
보상의 멱등성보상에도 트랜잭션 ID 기반 중복 제거를 넣는다보상이 두 번 돌아 재고가 두 번 복구된다
보상 실패 시 경로재시도 큐와 수동 개입 대시보드부분 성공 상태가 그대로 남는다

대가는 분명하게 적어 두는 편이 낫다. 격리성이 없다. PENDING 주문이라는 중간 상태가 외부에 노출되므로, 그 상태를 화면과 정책이 이름 붙여 다뤄야 한다.

그리고 실전에서 사고가 나는 자리는 정상 흐름이 아니라 보상의 멱등성이다. 정상 흐름은 개발 중에 수없이 밟아 보지만 보상 경로는 장애가 나야 처음 밟히고, 그때 재시도가 겹치면 두 번 실행된다. 표의 셋째 행과 넷째 행이 하나로 묶여 있는 이유다.

Q. 「CAP에서 CA를 선택했다」는 설명은 어떻게 읽히나

그 문장은 성립하지 않는다. 네트워크 분할은 설계자가 고를 수 있는 항목이 아니라, 분산 시스템이라면 반드시 일어나는 사실이기 때문이다.

따라서 CA를 골랐다는 말은 둘 중 하나를 뜻한다. 실제로는 분산 시스템이 아니거나(단일 노드 RDB), 분할이 일어났을 때 어떻게 동작할지를 아직 정하지 않았다는 뜻이다. 뒤쪽이 훨씬 흔하고 훨씬 위험하다.

그래서 확인할 것은 CAP 분류명이 아니라 구체적 동작이다.

확인할 것무엇이 갈리나
분할이 났을 때 소수파 노드가 쓰기를 거부하는가, 받아 두었다가 나중에 합치는가거부하면 일관성 쪽, 받아 두면 가용성 쪽이다
받아 둔다면 충돌 해소 규칙이 LWW인가 버전 벡터인가LWW는 동시에 들어온 쓰기 하나를 조용히 버린다
그 규칙으로 생기는 데이터 유실을 비즈니스가 감당할 수 있는가감당할 수 없다면 가용성을 골랐다는 결정 자체가 무효다
정상 상태에서 동기 복제로 일관성을 사고 있는가PACELC의 E 쪽 선택이다. 분할은 드물지만 지연은 매 요청 발생한다

네 행이 위에서 아래로 좁혀 들어간다. 분류를 동작으로 바꾸고, 그 동작의 규칙을 특정하고, 규칙의 결과를 비즈니스 언어로 옮기고, 끝으로 분할이 없는 평상시까지 질문을 넓힌다. 넷째 행을 빼면 거의 일어나지 않는 사건만 이야기하다 끝나는데, 사용자 체감을 만드는 것은 매 요청 붙는 지연 쪽이다.

Q. 서비스 초기에 샤딩을 미리 넣어 두는 것은 어떤가

권할 만하지 않다. 이유는 성능이 아니라 되돌릴 수 없다는 성질이다. 샤딩에는 롤백이 없고, 샤드 키를 바꾸려면 컬렉션 전체를 리샤딩해야 한다. 데이터가 적을 때 잘못 고른 샤드 키는 데이터가 많아진 뒤에 가장 비싼 부채가 되어 돌아온다.

게다가 초기에는 샤드 키를 잘 고를 정보 자체가 없다. 카디널리티도 데이터 분포도 쿼리 패턴도 실제 트래픽을 봐야 알 수 있는데, 그 전에 결정을 고정하는 셈이다.

순서무엇을 얻나되돌릴 수 있나
1. 파티셔닝스캔 범위와 관리 단위 축소. 트랜잭션과 조인은 그대로 남는다된다 — 같은 인스턴스 안의 일이다
2. 읽기 복제본읽기 처리량 분산된다 — 복제본을 떼면 원상태다. 대신 복제 지연이 따라온다
3. 샤딩진짜 Scale-Out안 된다

세 단계를 이 순서로 두는 근거는 오른쪽 열이다. 되돌릴 수 있는 수단을 먼저 다 써 보고, 그것으로 부족하다는 사실이 데이터로 확인된 다음에 되돌릴 수 없는 수단으로 넘어간다.

다만 미리 해 둘 것이 하나 있다. 샤드 키가 될 만한 컬럼을 애플리케이션이 항상 쿼리 조건에 싣도록 접근 경로를 정리해 두는 일이다. 지금 하면 비용이 들지 않고, 나중에 샤딩할 때 가장 큰 장벽을 미리 치워 둔다. 크로스 샤드 브로드캐스트가 다섯째 실패 모드로 올라와 있는 것이 이 준비를 건너뛴 결과다.

정리

이 시리즈 여섯 편을 관통하는 갈림은 셋이다.

갈림어디에서 나타나나
되돌릴 수 있는 결정인가샤딩, Kafka 파티션 수, 샤드 키 선정. 선택지 표에서 되돌림 불가를 대가로 적은 행은 하나뿐이고, 실패 모드 표의 앞 네 행이 그 값을 치른다
손실을 0으로 둘 것인가 지연을 낮출 것인가동기·반동기·비동기, 분할 시의 C와 A, 정상 시의 L과 C
보장을 프로토콜에 맡길 것인가 도메인 설계로 피할 것인가2PC 대신 Saga, 전역 순서 대신 키 단위 순서, 크로스 샤드 조인 대신 샤드 키를 실은 쿼리

세 번째가 가장 늦게 손에 잡힌다. 앞의 둘은 선택지가 표에 나열되어 있어 고르기만 하면 되지만, 세 번째는 문제를 푸는 대신 문제가 생기지 않도록 경계를 다시 긋는 일이라 기술 목록 어디에도 항목으로 적혀 있지 않다.

그래서 확장 설계를 마무리할 때 마지막으로 확인할 것은 「이 구조가 얼마나 확장되는가」가 아니라 **「지금 되돌릴 수 없는 것을 무엇으로 정했고, 그 근거가 실제 데이터인가」**다. 근거가 예측뿐이라면 그 결정은 아직 이른 것이고, 미룰 수 있다면 미루는 편이 거의 항상 싸다.