Foundry
키-값 저장소 설계
고급
핵심

값이 갈렸을 때 고르기

시각으로 고르면 조용히 하나가 사라진다

앞 단계에서 정족수가 순서를 정해 주지 않는다는 것을 봤습니다. 리더가 없으니 같은 키에 값이 둘 생길 수 있고, 이제 무엇을 최신으로 볼지 정해야 합니다.

시각으로 고르면 조용히 하나가 사라진다

두 쓰기가 같은 이전 값을 보고 시작하면 한쪽을 버릴 수 없다 둘 다 같은 값을 보고 고쳤다 장바구니 v1 사과 가: 사과, 배 나: 사과, 우유 v1 을 보고 썼다 v1 을 보고 썼다 시각으로 하나를 고르면 다른 하나가 사라진다 사용자는 담은 물건이 없어졌다고 느낀다 둘 다 v1 을 봤다는 사실이 기록되면 충돌임을 알 수 있다 알아채면 고를 수 있다. 합치거나 사용자에게 묻거나 모르면 조용히 하나를 버린다. 그것이 더 나쁘다

가장 단순한 방법은 시각이 늦은 값을 이기게 하는 것입니다. 구현이 쉽고 값이 하나로 정해집니다.

문제는 두 쓰기가 같은 이전 값을 보고 시작한 경우입니다. 위 그림에서 한 쪽은 배를, 다른 쪽은 우유를 담았습니다. 시각으로 하나를 고르면 다른 하나가 사라지고, 사용자는 담은 물건이 없어졌다고 느낍니다.

그리고 시각 자체를 믿기 어렵습니다. 노드마다 시계가 조금씩 다르므로 먼저 쓴 값이 늦은 시각을 가질 수 있습니다.

방식결과
시각이 늦은 값이 이긴다값이 하나로 정해진다. 한쪽이 조용히 사라진다
무엇을 보고 썼는지 기록한다충돌을 알아챌 수 있다. 정리 책임이 응용에 간다

무엇을 보고 썼는지 기록한다

값을 줄 때 그 값의 표시를 함께 주고, 쓸 때 그 표시를 되돌려 받습니다. 그러면 서버가 이 쓰기가 어느 값을 보고 만들어졌는지 알 수 있습니다.

읽기: 값 v1 과 함께 표시를 준다
쓰기: 새 값과 v1 표시를 함께 보낸다
서버: v1 뒤에 이미 다른 값이 있으면 충돌이다

충돌이면 두 값을 모두 보관하고 다음 읽기에서 함께 돌려줍니다. 그것을 어떻게 합칠지는 응용이 압니다. 장바구니라면 합집합을 취하면 되고, 설정값이라면 사용자에게 물어야 할 수도 있습니다.

알아채는 것이 고르는 것보다 중요하다

이 설계의 핵심은 충돌을 없애는 것이 아닙니다. 리더가 없으니 충돌은 생깁니다. 알아채지 못하는 것이 문제입니다.

상황사용자가 겪는 일
충돌을 모르고 하나를 버림담은 물건이 이유 없이 사라진다
충돌을 알고 합침둘 다 남는다
충돌을 알고 물어봄잠깐 번거롭지만 잃지 않는다

조용한 유실이 가장 나쁩니다. 사용자는 원인을 모르고, 우리도 지표에서 볼 수 없습니다.

그래도 시각으로 고르는 경우

값이 갈릴 일이 없거나, 갈려도 하나를 버려도 되는 데이터라면 시각 방식이 낫습니다.

마지막 상태만 의미 있는 값: 위치, 온도, 조회 수 갱신
이전 값을 읽고 계산해 쓰는 값: 장바구니, 잔액, 목록 편집

앞쪽은 시각으로 충분합니다. 뒤쪽에서 시각을 쓰면 사용자가 편집한 내용이 사라집니다. 데이터의 성격이 방식을 정합니다.

보관하는 값이 늘어나는 문제

충돌을 보관하면 값이 쌓입니다. 응용이 합쳐 주지 않으면 계속 늘어나므로, 합친 결과를 다시 써서 이전 것들을 정리하게 만들어야 합니다.

그리고 삭제가 어려워집니다. 값을 지우기만 하면 다른 사본에 남아 있던 옛 값이 되살아납니다. 그래서 삭제도 하나의 값으로 기록합니다. 다음 단계의 저장 구조가 이 성질과 맞물립니다.

면접에서 이렇게 나옵니다

Q.같은 키에 두 값이 생기면 무엇을 최신으로 보시겠습니까

무엇을 보고 썼는지를 기록해 충돌을 알아채고, 두 값을 함께 돌려줍니다.

값을 줄 때 그 값의 표시를 함께 주고, 쓸 때 그 표시를 되돌려 받습니다. 그러면 서버가 이 쓰기가 어느 값을 보고 만들어졌는지 알 수 있습니다.

읽기: 값 v1 과 함께 표시를 준다
쓰기: 새 값과 v1 표시를 함께 보낸다
서버: v1 뒤에 이미 다른 값이 있으면 충돌이다

합치는 방법은 응용이 압니다. 장바구니라면 합집합, 설정값이라면 사용자에게 묻는 것이 맞습니다.

흔한 실수: 서버가 알아서 하나를 고르게 만드는 것. 서버는 두 값의 뜻을 모릅니다. 뜻을 아는 곳에서 합치게 하고, 서버는 충돌이라는 사실만 정확히 전달하는 편이 옳습니다.

Q.시각이 늦은 값이 이기게 하면 무엇이 문제인가요

두 쓰기가 같은 이전 값을 보고 시작했을 때 한쪽이 조용히 사라집니다.

한 사용자는 배를 담고 다른 기기에서는 우유를 담았는데, 둘 다 같은 장바구니를 보고 시작했다면 어느 쪽도 틀리지 않습니다. 시각으로 하나를 고르면 나머지가 사라집니다.

그리고 시각 자체를 믿기 어렵습니다. 노드마다 시계가 다르므로 먼저 쓴 값이 늦은 시각을 가질 수 있습니다.

상황사용자가 겪는 일
충돌을 모르고 하나를 버림담은 물건이 이유 없이 사라진다
충돌을 알고 합침둘 다 남는다

흔한 실수: 시계를 잘 맞추면 된다고 답하는 것. 시계를 맞춰도 두 쓰기가 서로를 모르고 만들어졌다는 사실은 변하지 않습니다. 문제는 정밀도가 아니라 인과 관계입니다.

Q.시각 방식을 써도 되는 데이터는 무엇인가요

마지막 상태만 의미 있는 값입니다.

시각으로 충분: 현재 위치, 온도, 마지막 접속 시각
시각이 위험: 장바구니, 목록 편집, 이전 값을 읽고 계산해 쓰는 값

앞쪽은 갈려도 최신 하나만 남으면 됩니다. 뒤쪽에서 시각을 쓰면 사용자가 편집한 내용이 사라집니다.

데이터의 성격이 방식을 정합니다. 그리고 한 저장소 안에서 데이터마다 다른 방식을 줄 수 있습니다.

흔한 실수: 충돌 해소 방식을 저장소 전체에 하나로 정하는 것. 시각 방식은 값싸므로 쓸 수 있는 곳에서는 쓰는 편이 낫습니다. 모든 키에 충돌 보관을 강제하면 응용이 다뤄야 할 경우가 늘어나고, 대부분의 응용은 그것을 제대로 다루지 않습니다.

Q.충돌을 보관하면 값이 계속 늘어나지 않나요

늘어납니다. 응용이 합친 결과를 다시 써서 정리하게 만들어야 합니다.

합칠 때 "이 값들을 보고 썼다" 는 표시를 함께 보내면 서버가 이전 것들을 하나로 접을 수 있습니다. 응용이 합쳐 주지 않으면 값이 계속 쌓입니다.

그리고 삭제가 어려워집니다. 값을 그냥 지우면 다른 사본에 남아 있던 옛 값이 다시 살아납니다.

삭제도 하나의 값으로 기록한다
그 표시가 옛 값보다 뒤라는 것이 남아야 한다

흔한 실수: 삭제를 행 제거로 구현하는 것. 리더가 없는 구조에서 제거는 "이 값을 못 봤다" 와 구분되지 않습니다. 다른 사본이 옛 값을 들고 있으면 그것이 최신으로 되살아납니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

키-값 저장소 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.