25강
최종적 일관성
방금 저장했는데 왜 안 보이는가 — 그리고 그게 왜 대개는 괜찮은가
단계별로 보기
스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.
대기 중1 / 4 단계 — 저장은 즉시 끝난다
사용자가 사진을 바꿉니다. 원본에 기록되고 곧바로 완료 응답이 갑니다. 여기서 원본은 사본들이 그 내용을 받았는지 확인하지 않습니다. 확인을 기다렸다면 훨씬 느렸을 것입니다. 이 선택이 다음 장면의 문제를 만듭니다.
이 강의를 마치면
- 복제 지연이 존재하는 한 사본을 읽으면 옛날 값이 나올 수 있음을 설명한다.
- '최종적으로 일치한다'는 약속이 정확히 무엇을 보장하고 무엇을 보장하지 않는지 구분한다.
- 어떤 화면이 최신 값을 요구하고 어떤 화면이 조금 늦어도 되는지 판단한다.
- 자기가 방금 쓴 것을 자기는 봐야 한다는 요구를 어떻게 만족시키는지 설명한다.
"저장했는데 안 보여요"
사용자가 프로필 사진을 바꿉니다. "저장 완료"가 뜹니다. 그런데 새로고침하니 옛날 사진이 그대로 있습니다. 다시 새로고침하니 이번엔 새 사진이 나옵니다.
사용자는 확신합니다. "이 서비스 고장 났네."
버그가 아닙니다. 복제가 정상 작동한 결과입니다. 그리고 이 현상을 이해하지 못하면 절대 고칠 수 없습니다.
무슨 일이 일어났나
0ms 사진 저장 요청 → 원본에 기록 → "완료!" 응답
10ms 사용자가 새로고침 → 사본을 읽음 → 아직 옛날 사진 ❌
80ms 원본의 변경이 사본에 도착
200ms 사용자가 다시 새로고침 → 사본을 읽음 → 새 사진 ✓원본에 적힌 내용이 사본에 도착하기까지 이 예에서는 80밀리초가 걸렸습니다. 사용자는 하필 그 사이에 읽었습니다. 이걸 오래된 읽기(stale read)라고 합니다.
왜 시간이 걸릴까요. 사본이 다른 기계에 있기 때문입니다. 전기 신호가 케이블을 타고 가는 데에도 시간이 듭니다. 지연을 0으로 만드는 건 물리적으로 불가능합니다.
"최종적으로는 일치한다"
이 상태를 최종적 일관성(eventual consistency)이라고 부릅니다. 이름이 하는 약속을 정확히 읽어야 합니다.
약속하는 것: 더 이상 새로운 변경이 없다면, 언젠가는 모든 사본이 같은 값을 갖게 된다. 약속하지 않는 것: 그 '언젠가'가 언제인지. 그리고 그때까지 여러분이 무엇을 볼지.
두 번째 줄이 핵심입니다. 이 약속은 놀랍도록 약합니다. "결국엔 맞춰집니다"는 "지금 화면에 뭐가 보일지는 모릅니다"와 같은 말입니다.
더 이상한 일도 생깁니다. 사본이 여러 개면 어느 사본을 읽느냐에 따라 답이 다릅니다. 새로고침을 누를 때마다 새 사진과 옛 사진이 번갈아 나올 수 있습니다. 이걸 시간이 거꾸로 가는 읽기라고 부릅니다. 사용자에게는 유령이 나오는 것처럼 보입니다.
그런데 왜 이걸 견디는가
대안이 있긴 합니다. 원본이 모든 사본이 다 받았다고 확인할 때까지 응답을 미루는 것입니다. 그러면 지연이 사라집니다. 대신
- 쓰기가 가장 느린 사본만큼 느려집니다.
- 사본 하나가 죽으면 쓰기 전체가 멈춥니다.
즉 안전을 얻는 대가로 속도와 가용성을 냅니다. 그리고 대부분의 데이터에서는 이 거래가 손해입니다. 프로필 사진이 0.1초 늦게 보이는 것과, 사본 하나가 죽어서 서비스 전체가 멈추는 것 중 무엇이 더 나쁜가요.
그래서 화면마다 다르게 판단한다
"전부 최신"도 "전부 사본"도 답이 아닙니다. 화면마다 묻습니다.
| 화면 | 0.1초 늦으면? | 읽을 곳 |
|---|---|---|
| 다른 사람 게시글 목록 | 아무도 모른다 | 사본 |
| 상품 카탈로그 | 아무도 모른다 | 사본 |
| 조회수·좋아요 수 | 아무도 모른다 | 사본 |
| 내가 방금 쓴 글 | 고장으로 인식 | 원본 |
| 결제 직후 잔액 | 고객센터 전화 | 원본 |
| 재고 확인 후 주문 | 없는 물건을 판다 | 원본 |
아래 세 줄에는 공통점이 있습니다. 본인이 방금 바꾼 것을 본인이 확인하는 상황입니다. 남의 변경이 늦게 보이는 건 알아채지 못하지만, 내 변경이 안 보이면 즉시 알아챕니다.
이 요구에는 이름이 있습니다. "자기가 쓴 것 읽기(read-your-writes)"입니다. 가장 흔한 해법은 단순합니다.
무언가를 쓴 직후 몇 초 동안, 그 사용자의 읽기는 원본으로 보낸다.
전체 트래픽에서 이런 요청은 극소수라 원본에 부담이 거의 없습니다. 나머지 압도적 다수의 조회는 계속 사본이 처리합니다. 좁게 예외를 두는 것이지, 포기하는 게 아닙니다.
정리
복제 지연은 없앨 수 있는 버그가 아니라 관리해야 할 성질입니다. 설계자가 할 일은 지연을 0으로 만드는 게 아니라, 어느 화면이 지연을 견디지 못하는지 골라내는 것입니다. 그 화면만 원본으로 보내면 됩니다.
아래 과제에서 원본만 바라보다 무너지는 시스템을 사본으로 살리되, 최신이어야 하는 화면은 남겨 두는 판단을 직접 해보세요.
이 강의가 단순화한 것
교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.
전제한 것
- 복제 지연을 고정된 하나의 숫자로 둔다. 실제로는 쓰기 부하, 네트워크 상태, 사본의 처리 여력에 따라 밀리초에서 수 분까지 요동친다. 지연이 갑자기 튀는 순간이 사고의 대부분이다.
- 이 강의의 시뮬레이터는 '오래된 읽기'가 몇 건 발생했는지를 세어 보여주지만, 실제로 옛날 값을 돌려주지는 않는다. 값의 내용이 아니라 그런 읽기가 일어난다는 사실만 모델링한다.
- '자기가 쓴 것 읽기'를 위해 사용자별로 어느 요청이 최근에 쓰기를 했는지 추적하는 장치는 이 캔버스에 없다. 과제에서는 최신이 필요한 화면을 아예 별도의 경로로 나눠 표현한다.
- 복제는 비동기이며 사본이 밀리는 일은 없다고 본다. 현실에서는 쓰기가 몰릴 때 사본이 따라잡지 못해 지연이 계속 벌어지기도 한다.
다루지 않은 것
- 일관성 모델의 계층(선형화 가능성, 순차 일관성, 인과 일관성)
- CAP 정리와 PACELC
- 정족수(quorum) 읽기·쓰기와 R + W > N 조건
- 벡터 시계와 충돌 해결, CRDT
더 읽을거리: Designing Data-Intensive Applications — Chapter 5: Replication · Werner Vogels — Eventually Consistent
이제 직접 만들어 보세요
읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.
이 강의에 물어보기
하루 5번이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.