복제
DB가 한 대뿐이면 장애가 났을 때 서비스가 완전히 멈추고, 읽기 트래픽이 몰리면 그 한 대가 병목이 됩니다. 복제(Replication)는 Primary의 데이터를 하나 이상의 Replica에 계속 복사해 두는 것으로, Replica는 장애 대비책이자 읽기 부하를 나눠 받는 창구가 됩니다.
핵심 개념
Primary-Replica 구조
Primary가 모든 쓰기를 전담하고, Replica는 Primary의 변경사항을 복사해 읽기 전용으로 서비스합니다.
핵심 개념
동기 복제 (Synchronous)
Primary가 Replica의 쓰기 확인(ack)까지 기다린 뒤에야 클라이언트에 커밋을 알립니다. 유실 위험은 낮지만 느립니다.
핵심 개념
비동기 복제 (Asynchronous)
Primary가 Replica의 확인을 기다리지 않고 바로 커밋을 알립니다. 빠르지만 장애 시 최근 데이터가 유실될 수 있습니다.
핵심 개념
복제 지연 (Replication Lag)
Replica가 Primary를 따라잡는 데 걸리는 시간차입니다. 비동기 복제에서 특히 커질 수 있습니다.
핵심 개념
읽기 확장 (Read Scaling)
읽기 쿼리를 여러 Replica로 분산시켜 Primary의 부하를 줄이는 전략입니다.
핵심 개념
장애 조치 (Failover)
Primary에 장애가 발생하면 Replica 중 하나를 새 Primary로 승격시켜 서비스를 이어갑니다.
복제는 어떻게 이루어지나요
많은 관계형 DB는 Primary에서 일어난 변경을 로그로 기록하고, Replica가 그 로그를 받아 같은 순서로 적용하는 방식으로 복제합니다. PostgreSQL은 WAL(Write-Ahead Log)을 스트리밍으로 전달하고, MySQL은 바이너리 로그를 씁니다. 복제 모델은 쓰기를 받는 곳이 하나인 단일 리더, 여러 곳인 다중 리더, 정해진 리더가 없는 리더리스로 나눕니다. 이 토픽은 단일 리더(Primary-Replica)를 다룹니다.
자주 하는 오해
면접에서 이렇게 물어요
면접 질문
복제 지연이 있을 때 사용자가 방금 쓴 글이 안 보이는 문제는 어떻게 해결하나요?
자신이 쓴 데이터를 읽을 때만 Primary에서 읽게 하거나(read-your-writes), 쓴 직후 일정 시간은 Primary로 읽기를 보냅니다. 같은 사용자의 읽기를 항상 같은 Replica로 보내면 시간이 거꾸로 흐르는 것처럼 보이는 문제도 줄일 수 있습니다.
면접 질문
동기, 비동기, 반동기 복제를 비교해 보세요.
동기는 Replica 확인까지 기다려 유실 위험이 낮지만 느리고, 비동기는 기다리지 않아 빠르지만 장애 시 최근 데이터를 잃을 수 있습니다. 반동기는 Replica 한 곳이 받았다는 확인만 기다리는 절충안으로 일부 DB가 지원합니다.
면접 질문
스플릿 브레인이란 무엇인가요?
네트워크 단절 등으로 둘 이상의 노드가 자신이 Primary라고 믿어 서로 다른 쓰기를 받는 상황입니다. 데이터가 갈라지므로, 과반수 투표나 이전 Primary 격리(펜싱) 같은 방법으로 막습니다.
면접 질문
복제와 샤딩은 무엇이 다른가요?
복제는 같은 데이터를 여러 서버에 복사해 읽기 확장과 장애 대비를 얻고, 샤딩은 데이터를 나눠서 서버마다 일부만 저장해 용량과 쓰기를 확장합니다. 두 가지를 함께 쓰는 구성도 있습니다.
실제 시스템에서는
함께 보면 좋은 토픽
참고 자료
PostgreSQL — 스트리밍 복제 기본 설정
-- Primary: postgresql.conf wal_level = replica max_wal_senders = 10 synchronous_commit = on -- 동기 복제 시 'remote_write' 이상으로 설정 synchronous_standby_names = '' -- 동기 복제 사용 시 Replica 이름 지정 -- Replica: standby.signal 파일 생성 + primary_conninfo 설정 -- pg_basebackup으로 Primary 데이터를 복제한 뒤 Replica로 기동
synchronous_standby_names가 비어 있으면 비동기 복제입니다. 여기에 Replica 이름을 지정하면 Primary가 그 Replica의 ack을 기다리는 동기 복제로 바뀝니다 — 이 시각화의 두 모드 차이가 실제로는 설정 한 줄 차이입니다.
SQL — 복제 지연(Replication Lag) 확인하기
-- Primary에서 실행: 각 Replica가 얼마나 뒤처져 있는지 확인 SELECT client_addr, state, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes FROM pg_stat_replication; -- lag_bytes가 계속 커진다면 Replica가 Primary를 못 따라가고 있다는 신호
pg_stat_replication은 PostgreSQL이 각 Replica의 복제 상태를 보여주는 시스템 뷰입니다. 이 시각화의 "뒤늦게 기록" 스텝이 실제 운영 환경에서는 이 lag_bytes 값으로 관측됩니다.
Java — 읽기 쿼리를 Replica로 분산하기(개념)
// 쓰기는 Primary, 읽기는 Replica로 — 데이터소스 라우팅 개념 예시 public class ReplicaRoutingDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "replica" : "primary"; } } // Service 쪽에서는 @Transactional(readOnly = true) 여부만 신경 쓰면 됨 @Transactional(readOnly = true) public List<Topic> getPublishedTopicsPageable(...) { ... } // → replica로 라우팅
readOnly = true인 트랜잭션은 Replica로, 그 외에는 Primary로 보내는 방식입니다. 다만 방금 쓴 데이터를 바로 읽어야 하는 화면(예: 글쓰기 직후 상세 페이지)은 복제 지연 때문에 안 보일 수 있어 Primary로 강제 라우팅하는 예외 처리가 흔히 필요합니다.
Q1
동기 복제(Synchronous Replication)의 특징은?
Q2
비동기 복제에서 Failover 시 데이터가 유실될 수 있는 이유는?
Q3
복제 지연(Replication Lag)이 실무에서 문제가 되는 대표적인 상황은?