일관성 (Consistency)

‘빅데이터 세상으로 떠나는 간결한 안내서 NoSQL’를 읽고 정리하고자함


요점 정리

  • 쓰기 충돌
    • 투 클라이언트가 동시에 같은 데이터를 쓰려고 할 때 발생
  • 읽기-쓰기 충돌
    • 한 클라이언트가 쓰고 있는 도중에 다른 클리아온트가 비일관적 데이터를 읽을 때 발생
  • 방지 방법
    1. 비관적 방법 : 데이터 레코드에 잠금을 사용
    2. 낙관적 방법 : 충돌을 탐지해 해결
  • 분산 시스템에 읽기-쓰기 충돌이 발생하는 것은 업데이트를 받았지만, 다른 노드는 받지 못한 시점이 있기 떄문
  • 결과적 일관성
    • 쓰기가 다른 모든 노드로 전파되는 어떤 시점에 시스템이 일관성 있는 상태가 된다는 뜻
  • 클라이언트는 보통 자신이 쓴 것에 대한 일관성이 필요함
    • 즉, 클라이언트가 어떤 값을 쓴 즉시 그 값을 읽을 수 있어야 함
    • 읽기와 쓰기가 다른 노드에서 수행된다면 이를 제공하기 어려울 수 있음
  • 좋은 일관성을 얻으려면, 데이터 연산에 많은 노드가 관여해야 하지만, 이렇게 되면 지연이 길어짐
  • 따라서 보통은 일관성과 지연 사이에 타협해야함 ( Consistency <> Latency TradeOff )

  • CAP 정리는 네트워트 분단이 발생하는 경우, 데이터의 가용성과 일관성 사이에서 절청해야함을 알려줌
    • 지속성 또한 지연과 절충할 수 있음
    • 특히 데이터 복제에 실패하는 경우에도 서비스가 중단되지 않기를 바랄 경우 ( Durability <> Latency TradeOff )
  • 강력한 일관성을 유지하려고 모든 복제본에 접근할 필요는 없다
    • 충분한 정족수(quorum)만 채우면

클러스터 지향 NoSQL 데이터베이스로 이동하면서 생긴 가장 큰 변화는 ‘일관성’에 대한 생각

  • ‘CAP 정리’나 ‘결과적 일관성’ 같은 용어도 등장
  • 무엇이든 만들기 시작하면 시스템에 ‘어떤 종류의 일관성’이 필요한지 생각해야함

1. 업데이트 일관성 ( 쓰기 충돌 write-write conflict )

  • 쓰기 충돌 (write-write conflict)
    • 여러 사람이 동시에 같은 데이터 항목을 업데이트하는 문제
  • 쓰기 요청이 서버에 도달하면, 이 요청을 직렬화해 하나를 먼저 적용하고 그 다음 다른 하나를 적용
    • 즉, 직렬화한다는 것은 ‘순서대로 정렬해 일렬로 세움’ 이라고 생각할 수 있음
  • 두명의 사람 M,P가 동시에 전화번호를 각각 다른 형식으로 업데이트 했다고 가
  • 동시성 제어가 없다면, M의 업데이트는 적용된 즉시, P의 업데이트로 덮어쓰일 것
  • 이 경우 M의 것은 ‘업데이트 손실(lost update)’이 될 수 있고, 이 상황은 일관성이 깨진 것으로 볼 수 있음
    • P의 업데이트가 M의 업데이트 이전의 상태에 근거하고 있으면서 M의 업데이트 이후에 적용되었기 때문
  • 동시성 상황에서 일관성을 유지하는 두가지 방법
    1. 비관적 방법
    2. 낙관적 방법
  1. 비관적 방법
    • 충돌이 발생하는 것을 방지하는 방식으로 동작
    • 쓰기 잠금(write lock)
    • 값을 변경하려면 먼저 잠금을 얻어야하고, 한번에 한 클라이언트만 잠금을 얻을 수 있음을 시스템이 보장함
    • 위의 상황에서는 M 요청만 성공하고, P는 이후 수정을 다시 시도할지 경정하기 전에 M이 업데이트한 결과를 보게 됌
  2. 낙관적 방법
    • 충돌이 발생하도록 놔두고 충돌이 발생하면 이를 탐지해 적절한 조치를 취함
    • 클라이언트가 업데이트를 하기 전에 자신이 마지막으로 읽은 시점 이후로 값이 변경되었지는 검사하는 조건적 업데이트(conditional update) 사용하는 것
    • M의 업데이트는 성공하지만 P의 업데이트는 실패하고, P는 에러를 보고 값을 다시 확인 후 업데이트 계속 진행할지 결정
    • 두 업데이트를 모두 저장한 다음 충돌이 발생했다고 표시하는 경우도 있음
      • 버전관리 시스템에서 익숙한 방법, 두 업데이트를 병합해야하고, 사용자에게 두 업데이트를 보여주고 정리하게끔 함
  • 두 방법 모두 업데이트를 일관적으로 ‘직렬화’하는 것에 의존
  • 단일 서버에서는 이 작업이 명확하다, 그냥 하나를 고른 다음 다른 것을 고르면 됌

  • 그러나, 피어-투-피어 복제처럼 서버가 한대 이상이라면, 두 노드가 업데이트를 다른 순서로 적용될 수 있고, 그 결과 각 노드마다 다른 데이터값이 있을 수 있음
  • 분산 시스템에서 동시성을 말할 때는 보통, 모든 노드가 같은 순서로 연산을 적용하는 것이 보장되는 ‘순차적 일관성’을 뜻하는 것

  • 동시성 프로그램에서는 기본적으로 ‘안정성’과 ‘응답성’사이에서 선택해야함
    • 안정성 : 업데이트 충돌같은 에러를 회피하는 것
    • 응답성 : 클라이언트에 빠르게 응답
  • 비관적 접근법은 응답성을 저해하고, 비관적 동시성에서는 deadlock를 초래하는 일이 흔하고 이는 예방, 디버깅도 어려움
  • 복제를 사용하면 쓰기 충돌이 일어날 확룰이 훨씬 높아짐!
    • 두 개 이상의 노드가 어떤 데이터에 대한 복사본을 가지고 있고 이 데이터를 독립적으로 업데이트할 수 있다면,
    • 충돌을 피하기 위해 특별한 조치를 취하지 않은 한 충돌이 발생할 것
  • 데이터에 대한 쓰기를 단일 노드에서 처리하면 업데이트 일관성 유지가 휠씬 쉬움
  • 그래서 피어-투-피어 복제를 제외한 모든 모델이 ‘데이터에 대한 쓰기’는 단일 노드에서 처리함

2. 읽기 일관성

  • 데이터 저장소를 읽은 모든 클라이언트가 자신의 요청에 대해 항상 일관된 응답을 받는다는 보장은 없음
  • 비일관적 읽기(inconsistent read) 또는 읽기-쓰기 충돌(read-write conflict)

  • 다른 데이터 항목이 함께 의미를 가지는, 상품과 가격과 같이, 것을 보장하는 것을 ‘논리적 일관성’
  • 데이터베이스는 트랙잭션 개념으로 비일관적인 읽기-쓰기 충돌을 피함

  • NoSQL 데이터베이스는 트랙잭션을 지원하지 않으므로 일관성도 지원하지 않다는 주장도 있음. 하지만 다음 두가지 이 주장에 대해 답변이 될 수 있음
    1. 트랙잭션이 없다는 것은, ‘집합 지향 데이터베이스’에만 해당되는 것
      • 그래프 데이터베이스 경우 관계형 데이터베이스와 마찬가지로 ACID 트랙잭션을 지원
  1. 집햡 지향 데이터베이스는 원자적 업데이트를 지원하지만, 한 집합에 대해서만 지원
    • 여러 집합간의 논리적 일관성은 지원하지 않지만, 한 집합내에서는 지원함
  • 물론 모든 데이터가 같은 지합에 들어가는 것은 아니고, 여러 집합에 영향을 미치는 업데이트가 있는 경우, 비일관적 읽기를 수행할 수 있음
  • 비일관성이 존재하는 시간의 길이를 ‘비일관성 윈도(inconsistency window)’라함
    • NoSQL은 비일관성 원도는 매우 짧으먀, 아마존 문서에 따른 ‘simpleDB’ 서비스의 비일관성 윈도는 1초 미만이라고 함
  • 비일관적 읽기는 ‘복제’를 도입하면서 완전히 다른 종류의 비일관성이 존재함 = ‘복제 일관성’
    • 같은 데이터 항목을 다른 복제본에서 읽어도 같은 값임을 보장하는
  • 세션 일관성을 제공하는 방법
    1. 스티키 세션 (sticky session)
      • 세션을 유지되는 동안은 한 노드만 사용하는 방법으로, 세션을 유지하는 한 자신이 쓴 것에 대해 일관성은 보장됌
      • 단점은 부하 분산이 제대로 되지 않을 수 있음
  1. 버전 스탬프
    • 데이터 저장소와의 모든 상호작용에 세션이 사용한 가장 최근의 버전 스탬프를 포함하도록 하는 것
    • 서버 노드는 요청에 응답하기 전에 해당 버전 스탬프가 포함된 업데이트가 반영되었음을 보장해야함
  • 읽기 성능을 높기이 위해 슬레이브에서 읽기를 수행하게 하지만, 쓰기는 여전히 마스터에서만 수행해야 할 경우 ‘스티키 세션’과 ‘마스터-슬레이브 복제’로 세션 일관성을 유지하는 것이 까다로울 수 있음
  • 방법
    1. 쓰기를 슬레이브로 보내되, 슬레이브가 클라이언트에 대한 세션 일관성을 유지하면서 쓰기를 마스터로 전달하는 것
    2. 쓰기를 할 때만 임시로 세션을 마스터로 전환해 슬레이브가 마스터의 업데이트를 모두 따라잡을 때까지만 읽기도 마스터에서 수행
  • 데이터 저장소 관점에서 복제 일관성에 대해 살펴봤지만, 이는 애플리케이션 설계에서도 중요한 요소

3. 일관성 완화

  • 비일관성이 발생하지 않도록 시스템을 설계하는 것은 가능하지만, 시스템의 다른 특성을 수용할 수 없을 정도로 희생하지 않고는 불가능한 경우가 있음
  • 관계형 데이터베이스에서는 트랙잭션으로 강력한 일관성을 보장하지만, 트랙잭션 시스템에는 보통 격리 수준(isolation level)을 완화할 수 있는 기능이 있음
    • 아직 커밋되지 않은 데이터를 쿼리에서 읽도록 허용할 수 있고, 실제 성능 개선을 위해 가장 높은 격리 수준(직렬화)을 사용하지 않고 일관성을 완화해 사용함
    • 대부분 커밋된 데이터 읽기 트랙잭션 수준을 사용하는데, 이정도면 읽기-쓰기 충돌은 제거하지만 완벽하지는 않음
  • 트랙잭션으로 일한 성능 저하가 너무 심해 포기하는 시스템도 많고, 몇 가지 다른 방식으로 트랜잭션을 대체하는 경우도 있음

3.1 CAP 정리

  • NoSQL 세상에서 일관성읋 완화해야하는 이유로 ‘CAP 정리’를 인용하는 경우가 많음
  • CAP 정리의 기본은 ‘일관성(Consistency)’, ‘가용성(Availability)’, ‘분단 허용성(Partition tolerance)’ , 세 가지 속성 중 두가지만 취할 수 있다는 것

  • 가용성
    • 클러스터의 한 노드와 통신할 수 있으면 그 노드에서 읽기와 쓰기가 가능해야한다는 뜻
    • 시스템에서 실패하지 않은 노드가 받은 모든 요청은 응답해야함
  • 분단 허용성
    • 클러스터 내 통신 두절로 클러스터가 여러 조각으로 분단돼 서로 통신할 수 없게 되더라고 클러스터는 잘 동작해야 한다는 뜻
    • split brain 상태 - 좌뇌 우뇌 사이의 신경 연결이 끊어진 것에 비유
  • CA 시스템
    • 일관성과 가용성은 있지만, 분단 허용성은 없는 시스템 = 단일 서버 시스템
    • 단일 서버는 분할할 수 없으므로 분단 허용성에 대해 걱정할 필요없음
    • 서버가 살아 있으면 가용한 상태이며, 일관성을 유지하는 것, 대다수 관계형 데이터베이스 시스템이 살고 있는 세상
  • CAP 정리를 ‘셋 중 둘만 선택할 수 있다’ 라고 하지만, 실제는 분산시스템과 같이 분단이 발생할 수 있는 경우에는 ‘일관성’과 ‘가용성’사이에 절충해야 함

  • NoSQL 옹호자는 보통 NoSQL 시스템은 관계형 트랙잭션 ACID 속성을 그대로 따르는 대신 ‘BASE 속성’을 따른다고 말함
    • Basically Available, Soft state, Eventual consistency
  • 보통은 일관성과 가용성 사이의 절충보다는 일관성과 지연,Latency 사이의 절충을 고민하는 편이 나음
    • 요청을 처리할 때 더 많은 노드가 관여하게 하면 일관성을 높일 수 있지만, 관여하는 노드가 많아질 수 록 응답시간이 길어짐

4. 지속성 완화

  • 일관성의 핵심은 원자적, 독립적 작업 단위로 요청을 직렬화하는 것 ( ACID 속성 )
  • 데이터 저장소가 업데이트된 내용을 잃어버리리는 것은 무슨 의미가 있냐면 지속성 완화에 대해 비웃는 상황도 많음
  • 그러나, 높은 성능을 위해 지속성을 얼마간 희생하고 싶을 경우도 있음
    • 데이터베이스를 대부분 메모리에서 실행하고 업데이트 메모리에 적용하고 변경 사항을 주기적으로 디스크에 저장한다면, 높은 성능을 내지만
    • 서버가 갑자기 죽는다면 마지막 저장 이후의 변경 사항이 모두 날아간다는 대가가 따름

e.g) 사용자 세션 상태 저장하는 경우

  • 대규모 웹 사이트에는 사용자가 많고 각 사용자가 어떤 작업을 하고 있는지 세션 상태에 임시 정보를 유지
  • 이 상태에는 많은 활동이 있고 엄청난 요청을 만들어내며 웹사이트에서도 영향을 미침
  • 하지만 세션 데이터를 잃는 다는 것는 크리티컬한 상황이 아니기 때문에, 지속성 없는 쓰기의 좋은 후보

  • 데이터 복제 시에도 지속성을 어느정도 희생할 수 있음
    • 노드가 업데이트를 처리하다가 해당 업데이트가 다른 노드로 복제되기 전에 실패한 경우 ‘복제 지속성(replication durability)’ 실패가 발생할 수 있음
    • 마스터 슬레이브 구조에서의 마스터가 실패해, 해당 업데이트를 처리 못한 상황, 다시 복구되어도 이후 발생한 업데이트와 충돌이 발생함

5. 정족수

  • 요청에 많은 노드가 관여하면 비일관성을 피할 확률이 높아지는데..
  • 강력한 일관성을 얻으러면 얼마나 많은 노드가 관여해야하는가?

  • 쓰기 정족수 (write quorum)
    • 강력한 일관성 보장을 위해 모든 노드가 쓰기를 승인할 필요 없이 두개 노드(다수)만 승인하면 됌
    • 쓰기 충돌이 발생한 경우, 다수로부터 데이터를 얻을 수 있고 이를 ‘쓰기 정족수(write quorum)’ , W > N/2로 표현
    • W는 쓰기에 ㅊ팜여한느 노드 수, N은 복제에 관여하는 노드 수
  • 읽기 정족수 (read quorum)
    • 최신 변경 사항까지 얻으러면 얼마나 많은 노드에 접급해야하는가 ?
    • R + W > N
    • R : 읽을 시 확인해야하는 노드 수 , W : 쓰기 시 승인해야하는 노드 수 , N 복제 인수
    • 이 식은 피어-투-피어 분산 모델을 염두하고 작성한 것
    • 마스터 슬레이브는 쓰기 충돌을 회피하려면 마스터에만 쓰면 되구, 비슷하게 읽기-쓰기 충돌을 회피하려면 마스터에서만 읽으면 됌
  • 복제인수는 3이면 충분
    • 하나가 실패해도 읽기와 쓰기 정족수를 유지할 수 있음