복제와 데이터 분할은 무엇을 해결하는가¶
서버를 여러 대로 늘리면 용량과 장애 대응 능력을 얻지만, 복제 지연·충돌·재분배라는 새로운 상태가 생깁니다.
데이터베이스 한 대가 감당하기 어려워졌을 때 흔히 “분산하면 된다”고 말한다. 하지만 조회 요청이 많아서 어려운지, 저장 공간이 부족한지, 한 지역의 장애를 견뎌야 하는지에 따라 필요한 구조가 다르다. 복제는 같은 데이터를 여러 곳에 두고, 데이터 분할은 서로 다른 데이터를 여러 곳에 나눈다. 둘은 해결하는 문제가 다르다.
복제는 사본을 늘리고 최신성 문제를 만든다¶
Replication(복제)은 같은 데이터의 사본을 여러 노드에 유지한다. 주 노드가 변경을 받아 로그를 복제본에 전달하는 구조에서는 조회 요청을 복제본으로 나누고, 주 노드 장애 시 복제본을 승격할 수 있다.
대신 “어느 사본이 최신인가”라는 문제가 생긴다. 비동기 복제는 주 노드가 복제본의 반영을 기다리지 않아 쓰기 지연이 낮지만, 장애 직전 변경이 복제본에 도착하지 않았을 수 있다. 사용자가 방금 바꾼 프로필을 복제본에서 읽었는데 이전 값이 보이는 현상도 생긴다.
동기 복제는 하나 이상의 복제본이 로그를 받았다는 확인을 기다려 손실 범위를 줄인다. 하지만 네트워크 왕복이 커밋 지연에 들어오고, 복제본이나 네트워크가 느릴 때 쓰기까지 대기할 수 있다. “복제가 있다”보다 어떤 확인을 성공 조건으로 삼는지, 장애 조치 뒤 어느 시점까지 보존되는지를 확인해야 한다.
데이터 분할은 용량을 나누고 경계를 만든다¶
Partitioning(데이터 분할)은 키 공간을 나눠 서로 다른 데이터 조각을 여러 저장 위치에 둔다. 한 노드가 모든 사용자와 인덱스를 보관할 필요가 없어 저장 용량과 쓰기 처리량을 확장할 수 있다. 여러 서버에 나누면 흔히 샤딩이라고 부른다.
분할 키가 user_id라면 한 사용자의 데이터는 같은 조각에 모을 수 있다. 하지만 국가별 매출처럼 모든 조각을 읽는 질의는 결과를 다시 모아야 한다. 주문과 결제가 서로 다른 키로 나뉘면 한 트랜잭션으로 묶기 어려워진다. 특정 유명 사용자나 상품에 요청이 몰리면 한 조각만 뜨거워지는 핫 파티션도 생긴다.
분할은 처음 나누는 일보다 다시 나누는 일이 어렵다. 노드를 추가할 때 어느 데이터를 옮길지, 이동 중 조회와 쓰기를 어떻게 이어 갈지, 실패하면 어느 위치를 원본으로 볼지를 정해야 한다. 따라서 현재 병목이 저장 용량이나 한 노드의 쓰기 한계인지 측정하기 전에 분할하면 운영 복잡성만 먼저 얻는다.
flowchart TB
P["현재 병목 확인"] --> Q{"무엇이 부족한가?"}
Q -->|"조회 처리량·장애 대비"| R["복제: 같은 데이터의 사본 추가"]
Q -->|"저장 용량·쓰기 처리량"| S["데이터 분할: 키 공간을 여러 조각으로 분리"]
R --> R1["복제 지연과 장애 조치 기준 운영"]
S --> S1["분할 키·핫 파티션·재분배 운영"]
R1 --> C["보장과 운영 비용을 다시 측정"]
S1 --> C
CAP는 네트워크가 끊긴 동안의 결정이다¶
CAP는 Consistency(일관성), Availability(가용성), Partition Tolerance(네트워크 분할 내성)의 머리글자다. 여기서 네트워크 분할은 노드들이 실행 중이지만 서로 통신하지 못하는 상황이다. 실제 분산 시스템은 이런 상황이 없다고 보장할 수 없다.
그래서 “셋 중 둘을 고른다”는 문장은 지나치게 단순하다. 중요한 질문은 분할이 발생한 동안 오래된 값이나 충돌 가능한 쓰기를 성공으로 받아들일지, 일관성을 지키기 위해 일부 요청을 실패시킬지이다.
마지막 재고가 1개인데 서울과 부산 복제본의 연결이 끊겼다고 하자. 양쪽에서 주문을 모두 성공시키면 응답 가용성은 유지하지만 재고가 두 번 팔릴 수 있다. 한쪽만 쓰게 하거나 정족수 확인이 안 될 때 주문을 거절하면 재고 규칙은 지키지만 일부 사용자는 실패를 본다. 상품 설명은 잠시 오래된 값을 보여도 괜찮을 수 있지만 계좌 인출에는 같은 결정을 적용하기 어렵다.
BASE(Basically Available, Soft State, Eventually Consistent)는 대체로 응답 가능하고, 비동기 반영 때문에 상태가 변할 수 있으며, 새 변경이 멈추면 결국 사본이 수렴하는 계열을 설명한다. 하지만 “결국”이 몇 초인지, 충돌을 누가 해결하는지는 이 약어가 정하지 않는다.
ACID와 BASE를 서로 반대되는 제품 분류처럼 볼 필요도 없다. 한 시스템이 각 지역의 로컬 트랜잭션에는 ACID를 사용하면서 지역 간 복제는 최종 일관성을 사용할 수 있다. 설계 문서에는 약어 대신 최대 지연 시간, 충돌 규칙, 재처리 방법과 사용자에게 보이는 중간 상태를 적어야 한다.
목적별 저장소는 조회를 빠르게 하고 원본을 늘리지 않는다¶
원본 관계형 데이터베이스, 캐시, 검색 인덱스, 분석 저장소처럼 목적별 표현을 함께 두는 방식을 Polyglot Persistence(다중 저장소 구성)라고 부르기도 한다. 주문 원본은 관계형 데이터베이스에서 규칙을 지키고, 검색 인덱스는 상품명 검색을 빠르게 하며, 열 저장소는 대규모 집계를 담당할 수 있다.
여기서 캐시와 검색 인덱스를 새로운 원본처럼 다루면 안 된다. 어느 저장소가 최종 진실인지 정하고, 나머지는 원본 변경을 받아 재구축할 수 있어야 한다. 그렇지 않으면 서로 다른 값이 나왔을 때 어느 쪽을 고칠지 결정할 수 없다.
저장소 하나를 추가할 때마다 변경 데이터 전달, 허용할 최신성 지연, 중복과 순서 변경, 전체 재적재, 스키마 변경, 장애 알림과 담당자가 늘어난다. 조회 몇 밀리초를 줄이는 이득보다 이 상태들을 운영하는 비용이 큰지 확인해야 한다.
구조 변경을 검토하게 만드는 신호¶
- 조회 부하가 주 노드의 CPU·저장 대역폭을 지속적으로 포화시키고 복제본에서 안전하게 분리할 수 있다.
- 장애 조치 목표가 백업 복원 시간보다 짧아 대기 복제본이 필요하다.
- 데이터와 인덱스 증가 속도로 계산한 디스크 소진 시점이 운영 목표보다 가깝다.
- 한 노드의 쓰기 로그·잠금·저장 장치 한계가 실제 부하에서 확인됐다.
- 검색이나 분석이 거래 요청의 p99 지연과 복제 적용을 지속적으로 방해한다.
보편적인 데이터 크기나 초당 요청 수는 없다. 같은 초당 천 건도 한 행만 조회하는지, 인덱스 여러 개를 업데이트하는지, 한 키에 몰리는지에 따라 다르다. 변경 전후에 p95·p99 지연, 로그 생성량, 복제 지연, 핫 키 분포, 디스크 증가량과 복구 시간을 함께 기록해야 한다.