카테고리 없음

[NOSQL] NOSQL의 일관성에 대해 알아보자

busybeans 2021. 8. 27. 17:06
728x90
반응형

 

관계형 데이터베이스 (RDB)에서는 여러 비일관성을 회피하는 강력한 일관성을 지원합니다. 하지만 클러스터 지향 NoSQL 데이터베이스로 변환하면서 가장 큰 한 가지는 일관성을 생각하는 방식입니다. 

 

💻 업데이트 일관성 

두 사람이 동시에 같은 데이터 항목을 업데이트하는 문제를 쓰기 충돌(Write-Write conflict)이라고 합니다. 쓰기 요청이 서버에 도달하면 서버는 이 요청을 직렬화해 하나를 먼저 적용하고 그다음 다른 하나를 적용합니다. 만약 이런 동시성 제어가 없다면 먼저 온 업데이트 요청이 적용된 즉시 다음 업데이터로 덮어 쓰일 것입니다. 이 경우 앞선 업데이트 요청은 업데이트 손실(lost update)이 될 수 있습니다. 이런 상황을 일관성이 꺠진 것으로 볼 수 있는데, 이후 업데이트가 이전 상태에 근거하고 있으면서 이전 업데이트 이후에 적용되었기 때문입니다.

 

동시성 상황에서 일관성을 유지하는 방법은 비관적 접근법낙관적 접근법이 있습니다. 

 

비관적 접근법은 충돌이 발생하는 것을 방지하는 방식으로 동작합니다.

낙관적 접근법은 충돌이 발생하도록 놔두고 충돌이 발생하면 이를 탐지해 적절한 조치를 취합니다. 

 

자세히 말하자면 비관적 접근법은 쓰기 잠금(Write Lock)을 사용하는 것입니다. 값을 변경하려면 먼저 잠금을 얻어야 하고, 한 번에 한 클라이언트만 잠금을 얻을 수 있음을 시스템이 보장하도록 합니다. 예를 들어 두 명의 사용자 중 먼저 요청한 사람에게 쓰기 잠금을 주고 업데이트 요청을 성공하도록 합니다. 그다음 이후에 요청한 사람에게는 이후 수정을 다시 시도할지 결정하기 전에 이전 요청이 쓴 결과를 보게 됩니다. 낙관적 접근법은 클라이언트가 업데이트를 하기 전에 자신이 마지막으로 읽은 시점 이후로 값이 변경되었는지 검사하는 조건적 업데이트(conditional update)를 사용하는 것입니다. 예를 들면 첫 요청이 실패되면 이후에 요청한 것도 실패하도록 하는 것입니다. 

 

앞서 설명 드린 방법은 모두 직렬 화하는 것에 의존합니다. 단일 서버에서는 이 작업이 명확할 것입니다. 그러나 peer-to-peer 복제와 같이 분산 시스템에서는 일관성을 생각하는 방식은 다릅니다. 두 노드가 업데이트를 다른 순서로 적용할 수 있고, 그 결과 각 노드마다 다른 값을 가질 수 있습니다. 분산 시스템에서 동시성을 말할 때는 보통 모든 노드가 같은 순서로 연산을 적용하는 것이 보장되는 순차적 일관성을 뜻하는 것입니다.

 

분산 시스템에서 쓰기 충돌의 낙관적 접근법은 두 업데이트를 모두 저장한 다음 충돌이 발생했다고 표시하는 방법이 있습니다. 다른 방법으로는 컴퓨터가 직접 병합을 수행하도록 할 수 있습니다. 쓰기 충돌은 자동 병합하는 것은 분야에 따라 매우 달라서 각 경우에 맞게 작성해야 합니다.

 

쓰기 충돌 문제를 처음 접하는 사람은 보통 충돌을 회피하도록 되어있는 비관적 접근법을 선호합니다. 하지만 이는 문제가 발생할 수 있습니다. 동시적 프로그래밍에서는 기본적으로 안정성응답성 사이에서 선택해야 합니다. 비관적 접근법은 교착상태(Deadlock)와 같은 심각한 응답성을 초래합니다. 이는 예방도 어렵고 디버깅도 어렵습니다. 

 

 

💻 읽기 일관성 

읽기 일관성은 데이터베이스를 읽는 모든 클라이언트가 자신 요청에 대해 항상 일관적인 응답을 받는다는 보장은 없다는 것을 알아야 한다는 것입니다. 이런 경우를 관계형 데이터베이스에서 예를 들어보겠습니다. 배송비 테이블과 상품 테이블이 있습니다. A가 상품을 주문했습니다. 이제 배송 테이블에 주문한 상품에 대한 배송비 테이블에 값을 업데이트해야 하는데 그전에 B라는 사람이 A의 배송비 테이블을 읽었습니다. 그 후에 배송비 테이블이 업데이트되면 B가 알고 있는 배송비와 실제 배송비는 값이 다릅니다. 이를 우리는 비일관적 읽기(inconsistent read) 또는 읽기-쓰기 충돌(read-write conflict)이라고 합니다. 

 

이렇게 다른 데이터 항목이 함께 의미를 가지도록 보장하는 것을 논리적 일관성이라고 합니다. 관계형 데이터베이스는 읽기-쓰기 충돌 또는 비일관적 읽기를 피하기 위해서 트랜잭션 개념을 지원합니다. A가 요청한 두개의 쓰기 요청을 한 트랜잭션으로 묶으면, B의 요청은 A의 두 요청이  처리가 되면 처리될 수 있도록 하는 것입니다. 

 

NoSQL 데이터베이스는 이런 트랜잭션을 지원하지 않으므로 일관성도 지원하지 않는다는 지적이 있습니다. 이런 지적 사항에 대한 첫번째 해명은 집향 지향 데이터베이스에만 해당하는 것입니다. 그래피 데이터베이스는 ACID 트랜잭션을 지원합니다. 두 번째는 집합 지향 데이터베이스는 원자적 업데이트를 지원하지만, 한 집합에 대해서만 지원합니다. 여러 집합 간의 논리적 일관성은 지원하지 않지만 한 집합 내에선 지원이 된다는 뜻입니다. 그러면 위에서 말한 예에서 상품 테이블과 배송비 테이블을 하나의 집합으로 만들면 비일관적 상태에 빠지는 것을 회피할 수 있습니다.

 

물론 모든 데이티가 같은 집합에 들어갈 수 있는 것은 아닙니다. 따라서 여러 집합에 영향을 미치는 업데이트가 있는 경우 클라이언트가 비일관적 읽기를 수행할 가능성은 미해결 상태로 남습니다. 비일관성이 존재하는 시간의 길이를 비일관성 윈도(inconsistency window)라고 하는데 NoSQL 시스템에서 비일관성 윈도는 매우 짧습니다. 

 

복제와 같은 분산 시스템에서는 완전 다른 종류의 비일관성을 만나게 됩니다. 예를 들어 어떤 상품의 재고가 하나 밖에 안 남았는데 여러 지역에서 확인한 결과 재고가 한 개뿐이라 A가 먼저 샀습니다. 그러나 B는 막 재고가 나간 것을 확인할 수 있었지만, C는 아직도 재고가 남은 것으로 확인할 수 있습니다. 이를 복제 일관성이라고 부르는 다른 형태의 일관성 위반입니다. 나중에 C도 이 업데이트가 전달되어 모든 노드에서 재고가 없다는 것을 확인되면 이를 결과적 일관성이라고 합니다. 특정 시점에서는 일관성이 불일치 할 수 있지만 결국에는 모든 노드가 값은 값으로 업데이트될 것임을 뜻합니다.

 

복제 일관성이 논리적 일관성과 독립적이라고 해도, 복제 일관성이 커지면 논리적 일관성이 악화될 수 있습니다. 예를 들어 네트워크 지연 때문에 비일관성 윈도가 길어지면 발생되는 것처럼 말입니다. 

 

일관성 보장은 애플리케이션 전역에 지정되는 것이 아닙니다. 보통 각 요청에 따라 일관성 수준을 필요한 정도로 지정할 수 있습니다. 이렇게 하면 문제가 되지 않는 대다수 요청에 대해서는 일관성 수준을 낮추고, 꼭 필요한 경우에만 일관성 수준을 높일 수 있습니다. 

 

결과적 일관성을 사용하는 시스템에서 일관성을 보장하는 방법은 세션 일관성, 즉 사용자 세션 내에서 자신이 쓴 것에 대한 일관성을 제공하는 것입니다. 어떤 이유에 의해 세션이 끝나거나 다른 컴퓨터에서 같은 시스템에 동시에 접근하는 경우 사용자는 일관성을 잃겠지만, 이런 경우는 상대적으로 드물 것입니다. 

 

이런 세션 일관성을 제공하는 기법에는 몇가지 있습니다. 1) 스티키 세션(sticky session)은 세션이 유지되는 동안 한 노드에만 사용하도록 하는 것입니다. 이 세션을 사용하면 세션을 유지하는 한 자신이 쓴 것에 대한 일관성은 보장됩니다. 단점은 부하 분산이 제대로 되지 않을 수 있다는 점입니다. 2) 버전 스템프를 사용해 데이터 장소와의 모든 상호작용에 세션이 사용한 가장 최근 버전 스탬프를 포함하도록 하는 것입니다. 서버 노드는 요청에 응답하기 전에 해당 버전 스탬프가 포함된 업데이트가 반영되었음을 보장해야 합니다. 

 

읽기 성능 향상을 위해 슬레이브에서 읽기를 수행하게 하지만 쓰기는 여전히 마스터에서만 수행해야 할 경우, 스티키 세션과 마스터-슬레이브 복제로 세션을 일관성을 유지하는 것은 까다로울 수 있습니다. 이를 처리하는 방법 하나는 쓰기를 슬레이브로 보내되, 슬레이브가 클라이언트에 대한 세션 일관성을 유지하면서 쓰기를 마스터로 전달하게 하는 것입니다. 다른 방법은 쓰기를 할 때만 임시로 세션을 마스터로 전환해 슬레이브가 마스터의 업데이트를 모두 따라잡을 때까지만 읽기도 마스터에서 수행하게 하는 것입니다.

 

⭐️ 일관성 완화

일관성은 좋은 것이지만 많은 시스템은 이를 희생하는 경우가 있습니다. 비일관성이 발생하지 않도록 시스템을 설계하는 것은 가능하지만, 시스템의 다른 특성을 수용할 수 없을 정도로 희생하지 않고는 불가능한 경우도 많습니다. 관계형 데이터베이스는 트랜잭션을 통해서 강력한 일관성을 보장해주는데 보통 고립 수준(isolation level)을 통해서 완화 할 수 있습니다. 이런 고립 수준 조절로 트랜잭션으로 인한 성능 저하가 심한 것을 트랜잭션을 포기하며 성능을 높이는 시스템도 많습니다.  

 

💫 CAP 정리 

CAP 정리의 기본은 일관성(Consistency), 가용성(Availability), 분단 허용성(Partition tolerance) 세가지 속성 중 두가지만 취할 수 있다는 것인데, 이 세 속성을 어떻게 정의하느냐에 따라 문제가 완전히 달라집니다. 

 

CAP에서 가용성은 클러스터의 한 노드와 통신할 수 있으면 그 노드에서 읽기와 쓰기가 가능해야 한다는 뜻이다. 분단 허용성은 클러스터 내 통신 두절로 클러스타가 여러 조각으로 분단돼 서로 통신 할 수 없게 되더라도 클러스터가 잘 동작해야 한다는 뜻입니다. 

 

CAP 정리를 셋 중 둘중만 선택할 수 있다 라고 하지만, 실제로 뜻하는 것은 분산 시스템과 같이 분단이 발생 할 수 있는 경우에는 일관성과 가용성 사이에서 절충해야 한다는 것입니다. 일관성을 약간 희생해 가용성을 조금 높이는 식으로 절충할 수 있습니다.  

 

 

⭐️ 지속성 완화

지속성 완화에 대해서는 사람들이 중요치 않게 생각한다. 데이터베이스가 업데이트된 내용을 잃어버린다면 무슨 의미가 있겠는가?

그러나 높은 성능을 위해 지속성을 얼마간 희생하고 싶은 경우도 있습니다. 데이터베이스를 대부분 메모리에서 실행할 수 있다면 업데이트를 메모리에 적용하고 변경 사항을 주기적으로 디스크에 저장한다면, 상당히 높은 성능을 제공 할 수 있습니다. 하지만 서버가 갑자기 다운되는 경우 마지막 저장 이후의 변경 사항이 모두 날라갈 수 있습니다. 

 

이런 지속성 완화의 다른 예는 물리적 장치로부터 전달되는 원격 계측 데이터를 포착하는 경우입니다. 서버가 죽는 경우 마지막 업데이트가 날라가는 한이 있더라도 데이터를 빠른 비율로 포착하는 것이 중요합니다. 

 

데이터 복제 시에도 지속성을 어느 정도 희생할 수 있습니다. 노드가 업데이트를 처리하다가 해당 업데이트가 다른 노드로 복제되기 전에 실패한 경우 복제 지속성(Replicaion Durability) 실패가 발생 할 수 있습니다. 이런 경우 마스터-슬레이브 분산 모델에서 마스터가 실패한 경우 슬레이브가 새로운 마스터를 지정하는 상황에서 발생할 수 있습니다. 마스터가 실패하면 복제본으로 전달되지 못한 쓰기는 사실상 잃어버린 것과 마찬가지입니다. 마스터가 다시 온라인 상태로 돌아왔을때 이런 업데이트는 이후 발생한 업데이트와 충돌합니다. 마스터가 승인했기 때문에 업데이트에 성공했다고 생각했지만 마스터 노드가 실패해 업데이트를 상싱했기 때문에 이런 상황은 지속성 문제라 할 수 있습니다. 

 

충분히 마스터를 온라인 상태로 복구할 수 있다고 확신하면, 슬레이브가 자동으로 대체 작동하지 않게 할 수 있습니다. 클라이언트에 승인 메시지를 보내기전에 슬레이브가 해당 업데이트를 승인할때 까지 마스터가 대기하게 해서 복제 지속성을 개선 할 수 있습니다. 그러나 이렇게 하면 업데이트 속도가 느려지고 슬레이브가 실패한 경우에는 클러스터가 동작하지 않게 될 것이 뻔합니다. 지속성이 얼마나 중요한지에 따라 기술적 타협이 발생합니다. 지속성을 기본 수준으로 하고, 각 호출에 대해 지속성 수준을 명시하는게 좋습니다.

 

 

💫  정족수(Quorum)

알관성이나 지속성을 희생하는 경우, 양자택일을 제안하는 것은 아니다. 요청에 더 많은 노드가 관여하면 비일관성을 회피할 확률이 더 높아집니다. 강력한 일관성을 얻으려면 얼마나 많은 노드가 관여해야하는가? 

 

어떤 데이터가 세 노드에 복제되는 경우를 생각해보자. 강력한 일관성 보장을 위해 모든 노드가 쓰기를 승인할 필요는 없습니다. 두 노드만 승인하면 됩니다. 쓰기 충돌이 발생한 경우, 다수로부터 데이터를 얻을 수 있습니다. 이것을 쓰기 정속수(write quorum)라고 하고, 부등식으로 W > N/2로 표현할 수 있습니다. 쓰기에 참여하는 노드 수(W)는 복제에 관여하는 노드 수(N)의 절반을 넘어야 한다는 뜻입니다. 복제본 수는 보통 복제 인수(Replicatrion Factor)라 부릅니다. 

 

쓰기 정속수와 비슷하게 읽기 정속수란 개념도 있습니다. 최신 변경 사항까지 얻으려면 얼마나 많은 노드에 접근해야 하는가? 읽기 정속수는 좀 더 복잡한데, 쓰기를 승인하는데 얼마나 많은 노드가 필요한지에 따라 달라지기 때문입니다. 

 

복제 인수가 3인 경우를 생각해봅시다. 모든 쓰기에 두 노드의 승인이 필요하다면(W=2) 최신 데이터 읽기를 보장하기 위해서는 최소한 두 노드에 접근해야 합니다. 그러나 쓰기에 한 노드만 승인하면 되는 경우라면(W=1) 최신 데이터를 얻기 위해 세 노드를 모두 확인해야 합니다. 이 경우는 쓰기 정속수를 채우지 못했기 때문에 업데이트 충돌이 있을 수 있지만 데이터를 읽을 때 충분히 많은 노드에서 확인한다면 충돌을 감지할 수 있습니다. 따라서 쓰기에 대해 강력한 일관성을 갖지 않더라도 읽기에서는 강력한 일관성을 얻을 수 있습니다. 

 

읽기 시 확인해야 하는 노드 수(R)와 쓰기 시 승인해야 하는 노드 수(W), 복제 인수(N) 사이의 관계는 간단한 부등식으로 표현할 수 있습니다. R+W > N 인 경우 강력한 읽기 일관성을 얻을 수 있습니다. 

 

이 부등식은 peer-to-peer 분산 모델을 염두에 두고 작성한 것입니다. 마스터-슬레이브 분산 모델에서 쓰기 충돌을 회피하려면 마스터에만 쓰면 되거, 비슷하게 읽기-쓰기 충돌을 회피하려면 마스터에만 읽으면 됩니다. 분산을 샤딩으로 처리하는 경우 100개의 노드를 가진 클러스터에서 복제 인수는 3만 가질 수 있습니다. 

 

실제로 전문가들은 좋은 복원력을 갖는데 복데 인수가 3이면 충분하다고 합니다. 복제 인수가 3이면 노드 하나가 실패해도 읽기와 쓰기 정족수를 유지할 수 있습니다. 자동 재분배(automatic rebalancing) 기능이 있다면 세 번째 복제복을 만드는 데 오래 걸리지 않을 것이므로 대체 복제 노드가 활성화되기 전에 두 번째 복제본을 잃을 확률은 매우 적다고 할 수 있습니다. 

 

보통 두 경우 모두 고려해야 합니다. 읽기에 빠른 속도와 강력한 일관성이 모두 필요하다면, 쓰기에서 모든 노드의 승인을 받도록 해서 읽기 시 노드 하나만 접근해도 되게 할 수 있습니다. 이렇게 하면 세 노드에 모두 접근해야 하므로 쓰기가 느려지고, 한 노드 실패도 허용할 수 없게 됨을 뜻합니다. 그러나 어떤 환경에서는 피할 수 없는 희생일 수도 있습니다. 

 

 


오늘은 관계형 데이터베이스에서의 일관성과 그와 다른 NoSQL의 일관성에 대해서 알아보았습니다.

요점을 정리하자면

  • 쓰기 충돌은 두 클라이언트가 동시에 같은 데이터를 쓰려 할 때 발생함.
  • 읽기-쓰기 충돌은 한 클라언트가 쓰고 있는 도중에 다른 클라이언트가 비일관적 데이터를 읽을때 발생함.
  • 비관적 방법은 충돌을 방지하려고 데이터 레코드에 잠금을 사용함. 
  • 낙관적 방법은 충돌을 탐지해 해결함.
  • 분산 시스템에 읽기-쓰기 충돌이 발생하는 것은 일부 노드는 업데이트를 받았지만 다른 노드는 받지 못한 시점이 있기 때문임.
  • 결과적 일관성이란 쓰기가 다른 모든 노드로 전파되는 어떤 시점에 시스템이 일관성 있는 상태가 된다는 뜻임.
  • 클라이언트는 보통 자신이  쓴 것에 대한 일관성이 필요함.
  • 클라이언트가 어떤 값을 쓴 즉시 그 값을 읽을 수 있어야함. 읽기와 쓰기가 다른 노드에서 수행된다면 이를 제공하기 어려움.
  • 좋은 일관성을 얻으려며느 데이터 연산에 많은 노드가 관여해야 하지만 이렇게 하면 지연이 길어짐. 따라서 보통은 일관성과 지연 사이에서 타협해야함.
  • CAP 정리는 네트워크 분단이 발생한 경우 데이터의 가용성과 일관성 사이에서 절충해야 함을 알려줌.
  • 지속성 또한 지연과 절충할 수 있음. 특히 데이터 복제에 실패하는 경우에도 서비스가 중단되지 않기를 바랄 경우 그럼.
  • 강력한 일관성을 유지하려고 모든 복제본에 접근할 필요는 없음

 

 

[REFERENCE]

해당 글의 모든 레퍼런스는 "NoSQL, 빅데이터 세상으로 떠나는 간결한 안내서" (프라모드 사달게이, 마틴 파울러 지음)을 알립니다.

https://coupa.ng/b 5 YzGZ

 

NoSQL 빅 데이터 세상으로 떠나는 간결한 안내서 (마스크제공)

COUPANG

www.coupang.com

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

728x90
반응형