CAP과 일관성으로 보는 분산 시스템 선택
분산 시스템에서 네트워크 파티션이 생기면 일관성과 가용성 중 무엇을 포기할지 선택해야 한다. CAP, PACELC, Consistency 모델을 기준으로 Aurora, DynamoDB, S3, ElastiCache의 선택 기준과 실무 함정을 정리한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
분산 시스템의 일관성 문제는 멀리 있는 개념처럼 보이지만, 브라우저 캐시에서 stale 데이터를 본 경험과 같은 모양을 가진다. Cache-Control: max-age=3600으로 캐싱된 HTML은 서버에서 이미 바뀌었어도 최대 1시간 동안 구버전으로 보일 수 있다. 분산 시스템에서는 이 문제가 클라이언트와 서버 사이가 아니라 서버 노드들 사이에서 발생한다. 그래서 Write 후 Read에서 데이터가 없을 때, 이것이 버그인지 Eventual Consistency 특성인지 구별해야 한다.
- 02
단일 데이터베이스 안의 ACID 트랜잭션은 강력하지만, 서비스가 여러 AZ, 여러 리전, 여러 저장소로 나뉘면 전제가 달라진다. 커밋된 값을 모두가 즉시 본다는 말은 이제 네트워크 상태에 의존한다. Gilbert & Lynch의 CAP 증명은 비동기 네트워크에서 일관성, 가용성, 파티션 허용을 동시에 만족할 수 없음을 보였다. 실무에서는 이 제약이 장애 시 요청을 거부할 것인지, 아니면 구버전 응답을 허용할 것인지의 선택으로 드러난다.
- 03
CAP 정리는 2000년 Eric Brewer가 제안하고 2002년 Gilbert & Lynch가 증명한 정리다. Consistency는 모든 노드가 동시에 같은 데이터를 보고, 읽기가 항상 최신 쓰기 결과를 반환한다는 뜻이다. Availability는 모든 요청이 최신 데이터 보장 없이도 응답을 받는다는 뜻이다. Partition Tolerance는 노드 사이 통신 단절이 발생해도 시스템이 계속 동작한다는 뜻이다. 분산 시스템에서 네트워크 파티션은 회피할 수 없기 때문에, 실제 선택은 P가 발생했을 때 C와 A 중 무엇을 포기하느냐다.
- 04
CP 선택은 파티션이 생겼을 때 일관성을 위해 일부 요청을 거부한다. 문서의 비유처럼 은행 ATM은 네트워크 단절 시 현재 서비스 불가를 안내할 수 있지만 잔액 불일치는 허용하지 않는다. Aurora Multi-AZ에서 Writer가 있는 AZ에 장애가 나면 Failover가 발생하고, 새 Writer가 선출되기 전까지 쓰기 요청은 실패할 수 있다. AWS 공식 문서 기준으로 Failover는 보통 60초 미만, 종종 30초 미만으로 설명된다. 이 시간은 불편하지만, 데이터 정확성을 지키기 위한 비용이다.
- 05
AP 선택은 파티션이 생겼을 때 가용성을 위해 구버전 데이터를 허용한다. 소셜미디어 좋아요 수처럼 노드마다 잠깐 다른 숫자를 보여도 나중에 맞춰지면 되는 데이터가 여기에 가깝다. DynamoDB는 기본적으로 Eventually Consistent Read가 기본값이고, 높은 처리량과 낮은 지연을 얻는 대신 최근 쓰기가 바로 보이지 않을 수 있다. 다만 AP라고 해서 언제나 아무 문제 없이 응답한다는 뜻은 아니다. 핫 파티션이 생기면 단일 파티션 한계인 3,000 RCU, 1,000 WCU를 넘어서 ProvisionedThroughputExceededException이 발생할 수 있다.
- 06
CA 시스템은 일관성과 가용성을 말하지만 파티션을 허용하지 않는다는 전제를 둔다. 그래서 실제로는 단일 노드나 단일 데이터센터 시스템에 가깝다. 단일 노드 RDB인 MySQL, PostgreSQL은 파티션 자체가 없기 때문에 CA처럼 볼 수 있지만, 단일 AZ 배포에서는 AZ 장애가 곧 전체 장애가 된다. 실무에서 CA 시스템을 쓰겠다는 말은 분산 배포를 포기하겠다는 뜻이다. 고가용성이 필요하면 CA는 선택지가 아니며, 결국 CP나 AP 중 하나의 비용을 받아들여야 한다.
- 07
CAP만으로는 부족한 지점이 있다. CAP는 네트워크 파티션이 발생했을 때의 선택을 설명하지만, 실제 시스템은 정상 상태에서도 지연과 일관성 사이에서 선택한다. Daniel Abadi가 제안한 PACELC는 이 한계를 보완한다. 파티션이 있을 때는 응답할 것인가 정확할 것인가를 고르고, 파티션이 없을 때도 빠르게 읽을 것인가 최신 쓰기를 확인할 것인가를 고른다는 관점이다. DynamoDB 기본 설정과 Cassandra, Riak은 PA/EL로 분류되고, ZooKeeper, etcd, CockroachDB는 PC/EC로 분류된다.
- 08
Aurora의 PACELC 위치는 조금 더 세밀하게 봐야 한다. 파티션 시에는 CP로 일관성을 우선하고, 정상 상태에서는 Multi-AZ 동기 복제로 EC, 즉 일관성을 우선한다. 다만 쓰기 지연이 약간 있다. Read Replica를 사용하면 낮은 지연을 얻는 EL 쪽으로 운영할 수 있지만, 이때는 복제 지연을 허용해야 한다. 같은 원리는 데이터베이스 밖에서도 반복된다. S3는 2020년 12월부터 모든 GET, PUT, LIST에 강한 read-after-write consistency를 기본 제공한다고 발표했지만, S3 Replication의 Cross-Region은 여전히 Eventually Consistent다.
- 09
Consistency 모델도 강도에 따라 나뉜다. Linearizable은 가장 강한 일관성으로, 모든 읽기가 가장 최근에 완료된 쓰기 결과를 반환한다. ZooKeeper의 Znode와 etcd의 분산 락이 사용 사례로 제시된다. Sequential Consistency는 각 프로세스의 연산 순서는 보장하지만 전역 실시간 순서는 보장하지 않는다. Read-Your-Writes는 내가 쓴 데이터는 내가 반드시 읽을 수 있게 하는 모델이고, Eventual Consistency는 충분한 시간이 지나면 모든 노드가 같은 값으로 수렴하지만 수렴 전에는 다른 값을 반환할 수 있는 모델이다.
- 10
AWS 서비스를 이 관점으로 보면 선택 기준이 선명해진다. Aurora는 CP, PC/EC로 볼 수 있고 Writer 직접 읽기는 Strong Consistency를 제공한다. 트랜잭션 ACID 보장, 복잡한 JOIN, 금융과 주문 데이터에 적합하다. DynamoDB는 AP, PA/EL이고 기본은 Eventual이며 Strong 옵션은 2배 비용이 든다. 높은 처리량, 글로벌 테이블, 세션이나 게임 상태에 맞다. ElastiCache는 Primary에서 Replica로 비동기 복제되므로 구버전 데이터가 가능하다. S3는 강한 일관성을 기본 제공하지만 Cross-Region 복제는 별도로 봐야 한다.
- 11
실무 의사결정의 핵심은 더 좋은 데이터베이스를 고르는 일이 아니라, 실패했을 때 어떤 거짓말을 허용할지 정하는 일이다. 주문 생성 직후 목록에서 주문이 빠지는 것은 UX 버그처럼 보일 수 있지만, 결제 승인 직후 잔액이 잘못 보이는 것은 금전 사고가 된다. 반대로 좋아요 수가 3초 늦게 맞춰지는 일을 막기 위해 모든 읽기를 강한 일관성으로 바꾸면 DynamoDB 기준으로 읽기 비용이 2배가 되고 글로벌 사용자의 지연도 늘어난다. 결제 처리와 상품 재고는 Aurora 쪽이 맞고, 상품 카탈로그 조회나 알림 로그는 DynamoDB 쪽이 맞을 수 있다.
- 12
대표적인 실패 포인트는 Write 직후 Read다. DynamoDB 기본 읽기는 최근 완료된 쓰기를 바로 반영하지 않을 수 있고, 잠시 뒤 재시도하면 최신 값을 반환한다. Create 후 Redirect to GET 패턴에서는 ConsistentRead가 필요할 수 있고, 단순 생성 응답이라면 DB를 다시 읽기보다 생성된 객체를 직접 반환하는 편이 낫다. 또 ConsistentRead: true를 전체 API에 일괄 적용하면 RCU가 2배로 늘어 비용이 폭증할 수 있다. Aurora Read Replica도 Writer와 비동기 복제되므로, 쓰기 직후 읽기가 필요한 API는 Writer 엔드포인트에 고정해야 한다.
- 13
운영에서는 지표를 통해 일관성 문제와 설계 문제를 구분해야 한다. DynamoDB에서 throttling 합계가 0이 아니라면 단순한 AP 선택 문제가 아니라 파티션 키 설계가 처리량을 한 물리 파티션에 묶었을 수 있다. 날짜, 고정 값, 순차 ID 같은 파티션 키는 skew를 만들기 쉽고, 랜덤 접미사를 붙이는 suffix sharding이 해결책이 될 수 있다. 하지만 쓰기 경로가 분산되면 읽기 경로가 scatter-gather 비용을 감당해야 하므로 부하 테스트가 필요하다. Aurora에서는 AuroraReplicaLag와 AuroraReplicaLagMaximum을 함께 보고, 1초를 넘는 지연은 Writer 전용 읽기 전환을 검토할 신호다.
- 14
정리하면 CAP은 네트워크 장애 시 일관성과 가용성 중 하나를 포기해야 한다는 언어이고, PACELC는 정상 상태에서도 지연과 일관성의 교환이 있음을 보여준다. CP 시스템은 장애 시 응답을 거부하더라도 데이터를 정확하게 유지하고, AP 시스템은 구버전 데이터를 허용하더라도 응답 가능성을 우선한다. Eventual Consistency는 시간이 지나면 수렴하지만 즉시는 아니며, Strong Consistency는 쓰기 직후 읽기가 최신값이어야 하는 경로에 필요하다. 이 차이를 데이터의 성격에 맞춰 고르는 것이 Aurora, DynamoDB, S3, ElastiCache 선택의 기준이다.
같은 레이어
L9에서 이어 듣기
- 설계 원칙을 운영 가능한 코드로 잇기 길이 미정
- Clean Architecture의 의존성 규칙 길이 미정
- DDD 기본기: 도메인 언어와 경계 설계 길이 미정
- Twelve-Factor App 운영 원칙 길이 미정
- MSA 패턴, 분리의 이득과 운영 비용 길이 미정
- Saga Pattern: 로컬 커밋과 역순 보상 길이 미정
- CQRS와 이벤트 소싱의 운영 경계 길이 미정
- TDD와 테스트 피라미드로 설계하는 테스트 전략 길이 미정
- 대규모 웹 크롤러의 큐, 정중함, 중복 제거 길이 미정
- API 계약으로 안전하게 서비스 경계를 진화시키기 길이 미정
- URL Shortener와 Rate Limiter로 보는 시스템 디자인 길이 미정