[Database] 최종 일관성 (Eventual consistency)

최종 일관성(Eventual consistency)은 데이터베이스, 특히 분산형 시스템에서 주요하게 다뤄지는 핵심 키워드 중 하나다.




일관성과 처리 성능

분산시스템에서 일관성은 참 어려운 주제다.
일단 쓰기가 발생하면, 특정 서버 노드에 먼저 쌓일 것이다.


그럼 당연히 나머지 노드에게도 복제를 할텐데

문제는 이 복제라는게 실시간 보장을 할 수가 없다는 것이다.
당연히 지연이 발생할 수 있고, 다른 Client끼리 동일한 값을 수정할 수도 있다.

그러면 어떤 Client가 write한 값을 어떤 Client는 볼 수 있고, Client는 보지 못할 수도 있다.

사소한 문제로 느껴질 수도 있지만, 실제 시스템 구현에 들어가면 중대한 버그와 데이터 부정합성으로 나타나기 쉽다. 결국에는 전부 데이터를 기반으로 또 데이터를 쌓거나 계산을 하게 되는 것이기 때문이다.

최종 일관성 (Eventual consistency)이란 이런 데이터 일관성 상실을 정당화하는 단어다. 아무튼 결과적으로는 다 맞춰지긴 하니까 부분적으로나마 일관성을 보장할 수 있다는 것이다.




충돌 시나리오와 "시간"

만약 같은 값 A 행에 대해서, 서로 다른 DB 노드가 동시에 update를 했다면 어떻게 해야할까?
강력하게 Lock을 걸거나 뭔가 write에 주체가 되는 master가 있다면 쉽지만, 각 DB 노드들이 평등한 경우에는 깔끔하게 처리하기가 쉽지 않다.

CassandraDB를 예로 들면, 상당수의 DB 구현체에서는, 각 write에 대해 타임스탬프나 버전을 부여한다. 그리고 최신 버전의 write만을 보존하고, 나머지를 전부 버린다.
문제는 각 서버의 시간이 정확하지 않을 수 있다는 것이다. 시계 동기화는 생각보다... 오차가 많다.
여기에는 잡다한 트릭들이 많은 편이고, 상세히 다루지는 않고 넘어가겠다.

CouchDB는 좀 특이한 방식으로 충돌이란 문제를 소거한다. 하나만 남기고 버리거나 하는게 아니라, 그냥 버전 단위로 전부 쌓아두는 append only 방식을 취한다. 그 이후에 어떻게 처리할지는 애플리케이션의 책임이다.




조정 가능한 일관성 (QUORUM)

Eventual consistency를 주장하는 DB들도 그게 문제라는 것은 잘 인지하고 있다.
그래서 일관성을 자유롭게 선택할 수 있는 선택권을 주기도 한다. 이를 통해 read after write로 발생하는 문제를 최소화할 수 있다.

CassandraDB의 경우에는 QUORUM 기반의 일관성 보장을 제공한다.
모든 노드의 절반에 복제되었을때 쓰기가 성공했다고 치고 넘어갈 수 있고, 한놈만 받아도 됐다고 치고 넘어갈 수도 있고, 전부 복제되어야 쓰기가 성공했다고 판단하게 할 수도 있다.

"전부 복제"를 거는게 아니면 전부 일관성 손실 위험은 존재한다. 그 확률을 낮출 뿐이다.
그렇다고 "전부 복제"를 걸면 그만큼 성능이나 처리량이 저하되기 때문에, 이건 권장하는 옵션은 아니다.




장점

네트워크 지연이나 장애에 영향을 덜 받는다. 데이터 동기화를 반드시 기다리지는 않기 때문이다.

동기화를 책임지는 중심 Master Node 없이도 병렬적으로 서버를 확장하기 좋은 구조가 된다.
Eventual consistency를 보장하는 DB들은 대부분 수평 확장이 잘 된다. 그에 따라 write 처리량도 잘 늘어나는 편이다.

실제 성능도 전반적으로 더 나은 편이다. 특히 write 성능에 있어서 더 그렇다. write 후 동기화를 기다리지 않거나 덜 기다릴 수 있기 때문이다.




단점

말은 최종 일관성이라고 하는데, 결국은 일관성이 보장되지 않는다는 소리나 마찬가지다. 저런 조어를 누가 했는지 모르겠다.

최종 일관성을 제공하는 DB들은 결국, 발생할 수 있는 엣지케이스들에 대해서 일관성 손실 가능성을 어느 정도 감수해야 하고, 일관성 부족으로 인한 문제를 애플리케이션 수준에서 가져가야 한다.




구현 사례

쓰기 처리량에 집중한, 쓰기 확장에 자유로운 분산 DB들은 대부분 최종 일관성에 기반한 write 구조를 구현한다.

AWS DynamoDB는 기본적으로 Eventual consistency를 제공한다. 읽어도 그게 최신이 아닐 수 있다는 것이다.
하지만 원한다면 강한 수준의 일관성 옵션을 사용할 수 있다. 대신 비용이 2배가 된다.
노드 관리를 우리가 할 수 있는게 아니라서 write할때 일관성을 제어하는건 없다.

CassandraDB는 기본적으로 Eventual consistency를 제공한다. 다만 상술했듯 write를 할 때 일관성 수준을 자유롭게 조절하는 것이 가능하다.

CouchDB는 위에서 설명한 것처럼 좀 특이한 방식으로 접근한다. 애초에 DB 수준에서 많은 보장을 하려 하지 않고, 일단 대충 다 쌓은 다음에 application에서 처리하도록 하는 방식이다. 게다가 quorum 기반의 복제 동기화 대기 기능도 제공하지 않는다. 복제가 된건지 아닌지 알 수가 없고, 완전한 비동기 복제다.



참조
https://en.wikipedia.org/wiki/Eventual_consistency
https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
https://guide.couchdb.org/draft/consistency.html
https://docs.couchdb.org/en/stable/intro/consistency.html