21강
데이터베이스 복제
쓰는 곳은 하나로 두고, 읽는 곳은 여럿으로 늘린다
먼저 보면 좋은 강의: 웹 요청의 해부, 지연 시간과 처리량, 데이터베이스 인덱스
단계별로 보기
스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.
대기 중1 / 4 단계 — 들여다보는 사람이 대부분이다
초당 430건 중 400건이 조회이고 30건만 저장입니다. 비율로는 열에 아홉이 읽기입니다. 대부분의 서비스가 이렇게 생겼습니다. 이 사실이 복제라는 해법의 출발점입니다.
이 강의를 마치면
- 복제본이 주 데이터베이스의 사본이며 읽기만 받는다는 것을 설명한다.
- 읽기와 쓰기를 서로 다른 경로로 보내는 설계를 직접 구성한다.
- 복제본에 쓰기를 보내면 왜 실패하는지, 그것이 왜 올바른 동작인지 설명한다.
- 복제가 읽기 부하만 해결하고 쓰기 부하는 해결하지 못함을 판단한다.
장부가 한 권뿐인 사무실
회사에 거래 장부가 딱 한 권 있습니다. 기록도 이 장부에 하고, 조회도 이 장부를 봅니다. 직원이 다섯 명일 땐 괜찮았는데 백 명이 되니 장부 앞에 줄이 섭니다.
그런데 줄을 자세히 보면 이상합니다. 백 명 중 아흔다섯 명은 그냥 들여다보러 왔습니다. 뭔가를 적으러 온 사람은 다섯 명뿐입니다.
이게 거의 모든 서비스의 모습입니다. 쇼핑몰에서 상품을 보는 사람은 수만 명이지만 실제로 주문하는 사람은 수백 명입니다. 읽기가 쓰기보다 압도적으로 많습니다.
그러면 사본을 만들면 되잖아
맞습니다. 그게 복제(replication)입니다.
- 원본 장부(주 데이터베이스) — 기록은 반드시 여기에만 합니다.
- 사본(복제본) — 원본을 그대로 베낀 것. 읽기만 받습니다.
원본에 뭔가를 적으면, 그 내용이 곧바로 사본들에도 전달되어 똑같이 적힙니다. 이제 들여다보러 온 아흔다섯 명은 사본 세 권에 나눠 서면 되고, 원본 앞에는 기록하러 온 다섯 명만 남습니다. 원본의 줄이 사라집니다.
왜 사본에는 못 쓰게 하는가
이 규칙이 핵심입니다. 사본은 쓰기를 거절합니다. 불편해 보이지만 반대를 상상해 보면 이유가 명확합니다.
사본 A에 "잔액 1만 원"이라고 적고, 같은 순간 사본 B에 "잔액 2만 원"이라고 적었다고 합시다. 어느 쪽이 맞습니까? 아무도 모릅니다. 원본조차 모릅니다.
기록하는 곳을 하나로 못 박아 두면 이 질문 자체가 생기지 않습니다. 순서는 원본이 정하고, 사본은 그 결정을 따라 적기만 합니다. 이걸 단일 기록점이라고 부릅니다. 사본이 쓰기를 거절하는 건 고장이 아니라 설계의 핵심입니다.
이 강의의 시뮬레이터에서도 복제본에 쓰기를 보내면 그 요청은 실패합니다. 과제에서 오류가 보인다면 버그가 아니라 이 규칙이 작동한 것입니다.
그래서 경로가 둘로 갈린다
쓰기 요청 ──────────────→ 주 데이터베이스 (원본)
│
│ 복제
▼
읽기 요청 ──────────────→ 복제본 (사본)애플리케이션은 이제 요청의 성격에 따라 목적지를 골라야 합니다. "이 요청은 뭔가를 바꾸는가?"가 판단 기준입니다.
| 목적지 | 늘릴 수 있나 | |
|---|---|---|
| 조회 | 복제본 | 예 — 복제본을 계속 추가 |
| 저장·수정·삭제 | 주 데이터베이스 | 아니오 — 원본은 한 개 |
복제가 해결하지 못하는 것
여기서 정직해야 합니다. 복제는 읽기 부하만 해결합니다.
복제본을 백 개로 늘려도 쓰기 처리 능력은 1도 늘지 않습니다. 오히려 원본이 백 개의 사본에 변경 내용을 전달해야 하니 더 바빠집니다.
쓰기가 감당이 안 되는 상황에서 복제본을 추가하는 것은 틀린 처방입니다. 그 문제는 다음 강의인 샤딩에서 다룹니다. 복제와 샤딩은 경쟁하는 기술이 아니라 서로 다른 병을 고치는 약입니다.
그리고 하나 더. 원본의 변경이 사본에 도착하기까지는 아주 짧지만 0이 아닌 시간이 걸립니다. 그 사이에 사본을 읽으면 방금 저장한 내용이 안 보입니다. 이 현상은 최종적 일관성 강의에서 따로 다룹니다.
이 강의가 단순화한 것
교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.
전제한 것
- 복제는 비동기다. 즉 주 데이터베이스는 사본이 다 받았는지 확인하지 않고 바로 사용자에게 응답한다. 실제로는 동기 복제(사본 확인 후 응답)도 있고, 그 경우 쓰기가 훨씬 느려진다.
- 이 강의에서는 복제 지연을 거의 0으로 두고 넘어간다. 지연이 만드는 문제는 '최종적 일관성' 강의에서 다룬다.
- 애플리케이션이 읽기와 쓰기를 각각 다른 경로로 보내도록 미리 나눠 두었다. 실제 시스템에서는 이 분기를 애플리케이션 코드나 프록시가 판단해야 하며, 그 판단을 틀리는 것이 흔한 사고 원인이다.
- 주 데이터베이스가 죽었을 때 사본 중 하나를 원본으로 승격하는 절차(failover)는 자동으로 일어나지 않는 것으로 본다.
다루지 않은 것
- 동기·준동기 복제와 쓰기 지연의 트레이드오프
- 장애 조치(failover)와 승격 절차, 스플릿 브레인
- 다중 주 복제(multi-leader)와 충돌 해결
- 복제 방식(문장 기반, 행 기반, WAL 전송)의 차이
더 읽을거리: PostgreSQL Documentation — High Availability, Load Balancing, and Replication
이제 직접 만들어 보세요
읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.
이 강의에 물어보기
하루 5번이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.