MongoDB Write Concern 와 Read Concern

Share

Last Updated on 4월 20, 2024 by Jade(정현호)

안녕하세요

이번 글에서는 MongoDB의 데이터를 쓰고 읽을 때 필요한 데이터의 동기화 기능인 Write Concern 와 Read Concern 기능에 대해서 확인해보도록 하겠습니다.

• 연관된 이전글

            

Write & Read Concern

일반적인 DBMS에서는 대부분이 분산 처리가 아닌 기본적으로 단일 노드의 처리에 중점을 두고 작동하는 아키텍처를 가지고 있거나 사용되며, 그에 따라 데이터를 쓰는 단일 노드에서의 트랜잭션 커밋(commit) 시에 디스크에 트랜잭션 로그에 대한 동기화 방법 등을 제어할 수 있었습니다.

또한 데이터를 읽을 때는 해당 노드에서 설정한 격리 수준(Isolation Level)에 따라서 커밋된 트랜잭션 데이터 중에서 선별적으로 데이터를 조회를 하였습니다.

MongoDB 서버에서는 분산 처리를 기본 아키텍처로 선택하고 있기 때문에 일반적인 RDBMS의 단일 노드에서의 격리 수준(Isolation Level) 구현뿐만 아니라 레플리카 셋을 구성하는 멤버들 간의 동기화까지 고려 또는 제어할 수 있어야 합니다.

그래서 MongoDB 서버는 데이터를 쓰고 읽을 때 필요한 데이터의 동기화(ACID에서 Durability, 지속성) 수준에 따라 데이터를 변경하거나 조회할 수 있도록 Read Concern과 Write Concern 옵션 및 기능을 제공하고 있습니다.

이러한 데이터 동기화 처리 기능은 MongoDB 서버 레벨이 아닌 클라이언트의 쿼리 수행 단위로 다르게 설정할 수 있습니다.
             

Write Concern

MongoDB 서버에서는 컬렉션에 도큐먼트를 저장할 때 사용자의 데이터 변경 요청에 응답이 반환되는 시점이 트랜잭션의 커밋으로 간주되며, 분산 데이터베이스 환경으로 설계된 MongoDB 서버임에 따라 사용자가 요청한 변경 사항이 어떤 상태까지 완료되면 응답을 내려 보낼지 신중하게 판단합니다.

이렇게 사용자의 변경 요청에 응답하는 시점을 결정하는 옵션을 MongoDB 서버에서는 Write Concern 이라고 합니다.
Write Concern은 이름에서도 알 수 있지만, 데이터 읽기와 무관하며 데이터의 INSERT 와 UPDATE 그리고 DELETE 오퍼레이션에서 사용되는 기능입니다.

MongoDB 서버에 접속하는 프로그램에서는 클라이언트와 데이터베이스 그리고 컬렉션 레벨의 3가지 방법으로 Write Concern을 설정할 수 있습니다.

Replica Set에 대한 Write Concern는 클라이언트가 요청한 작업에 대해서 성공되었다는 메세지를 반환하기전에 쓰기 작업에 대해서 승인해야 하는 구성원(주 및 보조)의 수를 설명합니다.

설정된 옵션에 따라서 구성원은 쓰기 작업을 성공적으로 수신하고 적용한 후에만 쓰기 작업을 승인할 수 있습니다.

즉, 수행한 쓰기 작업에 대해서 언제 acknowledgment 를 반환할지를 또는 클라이언트의 쓰기 요청에 응답하는 시점을 결정하는 옵션이라고 설명할 수 있습니다.

[crud-write-concern-w-majority]


MongoDB 레플리카 셋(Replica Set) 환경에서 Primary(프라이머리) 멤버가 비정상적 종료(Instance Crash)가 되었을 경우나 네트워크 연결이 끊기게 되면 세컨더리(Secondary) 멤버 중에서 새로운 프라이머리 멤버로 선출하게 됩니다.

이러한 레플리카 셋이나 MySQL에서 Replication 환경에서는 보통의 경우 Primary(DB에서는 Source/Master) 멤버는 변경 또는 입력된 데이터가 있지만, 세컨더리(DB에서는 Replica/Slave) 멤버에는 해당 데이터가 없을 수도 있습니다.

MongoDB에서는 프라이머리의 모든 OpLog가 세컨더리 멤버에서는 없을 수도 있습니다. 즉, 프라이머리의 최신 변경(입력된)된 내용이 세컨더리 멤버에는 아직 반영이 되지 않은 시점에서 프라이머리 멤버가 비정상적 종료가 되어 새로운 세컨더리 멤버가 프라이머리 멤버로 선출되면 가장 최신의 최종 데이터를 가지고 있지 않지만 그대로 프라이머리 멤버로써 사용해야 합니다.

향후에 이전의 프라이머리 멤버가 정상화되어 세컨더리 멤버로 조인하게 되면 자신이 더 최신의 데이터나 더 많은 데이터를 가지고 있지만 새로운 프라이머리 멤버의 OpLog를 통해 싱크를 맞추는 작업을 하게 되면서 더 많이 가지고 있던 또는 최신의 데이터는 롤백을 하고 새로운 프라이머리 멤버의 OpLog 으로 부터 동기화를 하게 됩니다.

쓰기를 승인하는 멤버가 많게 설정할수록(많은수록) 프라이머리 멤버 문제시 입력된(변경된) 데이터가 롤백될 가능성이 줄어듭니다. 그러나 클라이언트가 설정한 Write Concern 수준만큼의 승인을 받을 때까지 기다려야 하므로 Write Concern의 수를 높게 지정하면 쓰기 완료에 대해서 대기 시간이 길어질 수 있습니다.

데이터 입력/변경 작업에서 이상적인 Write Concern을 선택하는 것은 응용 프로그램의 성능 목표와 데이터 내구성 요구 사항에 따라 달라집니다.
          

Write Concern Specification

Write Concern 옵션은 다음과 같은 필드가 있습니다.

{ w: <value>, j: <boolean>, wtimeout: <number> }


w 옵션

쓰기 작업이 지정된 수의 mongodb 인스턴스 또는 특정 태그가 있는 mongod 인스턴스로 동기화되었다는 확인하는 옵션으로 쓰기 작업에 대해서 레플리카 셋에서 데이터를 동기화해야 할 멤버 수를 지정하거나 특정 태그명을 지정하거나 majority 로 설정합니다.

majority 는 레플리카 셋 멤버 중 과반수가 데이터 동기화가 되면 쓰기 작업이 안정적으로 커밋되었음을 클라이언트에게 알립니다.

여기에서 계산되는 멤버는 데이터를 보유한 멤버입니다. 즉, members[n].votes가 0보다 큰 primary와 secondary 멤버를 의미합니다.

예를 들어, Primary-Secondary-Secondary (P-S-S)로 구성된 3명의 투표 멤버가 있는 레플리카 셋 환경에서는 계산된 과반수는 두 개이며, Write Concern을 인정하기 위한 쓰기 작업 내용이 Primary와 한개의 Secondary로 전파되어야 합니다.

MongoDB 5.0 버전 부터 { w: "majority" } 가 Write Concern의 기본값이 되었습니다.

그러나 레플리카 셋에 Arbiter 멤버가 포함된 환경에서는 특별한 고려 사항이 있으며, 아래에서 내용을 다루고 있습니다.

쓰기 작업이 w: majority 을 통해 클라이언트에게 쓰기 완료 응답이 반환된 후, 클라이언트는 majority readConcern으로 해당 쓰기의 결과를 읽을 수 있습니다.

w:1
쓰기 작업이 Standalone Mongod 또는 레플리카 셋의 프라이머 멤버 Mongod에 전파되었다는 확인을 요청합니다. 쓰기 작업이 세컨더리 멤버에 복제되기 전에 프라이머리 멤버가 중단되면 데이터를 롤백 될 수 있습니다.
즉 w가 1일 경우 write concern 적용되지 않습니다.

w:0
쓰기 작업에 대한 승인을 요청하지 않습니다. 그러나 w: 0은 소켓 예외(exceptions) 발생 및 네트워킹 오류에 대한 정보를 애플리케이션에 반환할 수 있습니다. 쓰기 작업이 세컨더리 멤버에 복제되기 전에 프라이머리 멤버가 중단되면 데이터를 롤백 될 수 있습니다.
w: 0을 지정하고 j: true를 포함하면 j: true 옵션을 우선하며 Standalone Mongodd 또는 레플리카 셋의 프라이머 멤버 mongod로부터 승인을 요청합니다.

w 옵션에서 숫자가 높아질수록 롤백 가능성을 줄이고 데이터 일관성을 보장하지만 트레이드 오프 관계에 따라서 낮은 숫자에 비해 성능은 저하될 수 있습니다.


j 옵션

j 옵션은 쓰기 작업이 온디스크 저널에 기록되었다는 MongoDB의 승인을 요청합니다.

true인 경우 w: <value>에 지정된 mongod 인스턴스가 온디스크 저널에 썼다는 확인을 요청합니다.

j: true 자체만으로는 리플리카 셋의 프라이머리 멤버의 장애로 인한 쓰기가 롤백 되지 않는다는 것을 보장하지 않습니다.

MongoDB 버전 3.2에서 변경된 내용으로  j: true를 사용하면 MongoDB는 기본 멤버를 포함하여 요청된 수의 멤버가 저널에 작성된 후에만 반환합니다. (이전에는 프라이머리 멤버에서만 저널 쓰기만 하면 되었습니다.)

j(journal) 옵션은 입력(변경) 쓰기가 w 옵션에서 지정된 만큼 레플리카 셋 멤버에 동기화가 되고 디스크의 저널(journal)에도 완료된 경우 클라이언트에게 완료가 반환됩니다.

RDBMS에서 데이터 입력/변경시 트랜잭션 로그에 먼저 기록하는 것과 유사하게, 디스크의 저널에 먼저 기록하는 기능을 의미하며 MongoDB 서버가 비정상적 종료시에도 데이터 손실을 막을 수 있습니다.

다만 이 옵션(journal)만으로는 레플리카 세트에서 프라이머리 멤버의 비정상적 종료에 의한 쓰기 롤백 현상을 방지할 수 있다는 것을 보장하지는 않습니다.


wtimeout 옵션

이 옵션은 Write Concern에 대한 시간 제한을 밀리초 단위로 지정합니다. wtimeout은 1보다 큰 w 값에만 적용 가능합니다.

wtimeout는 Write Concern 옵션을 사용해 쓰기 작업에 대한 시간 제한을 밀리초 단위로 설정하는 옵션입니다.

wtimeout은 지정된 제한 시간이 지나면 필요한 write concern이 결국 성공하더라도 쓰기 작업이 오류와 함께 반환되게 합니다.

오류 발생시 MongoDB는 Write Concern이 wtimeout 시간 제한을 초과하기 전에 수행된 성공적인 데이터 수정을 취소하지 않습니다.

wtimeout 옵션을 지정하지 않고 Write Concern 수준이 달성 불가능한 경우, 쓰기 작업은 무한정으로 차단됩니다. wtimeout 값을 0으로 지정하는 것은 wtimeout 옵션이 없는 Write Concern과 동일합니다.
           

Implicit Default Write Concern

MongoDB 5.0부터는 implicit Write Concern 의 기본값은 w: majority 입니다. 그러나 arbiter 멤버가 포함된 레플리카 셋에서는 특별한 고려 사항이 있습니다

레플리카 셋의 투표 과반수(voting majority)는 투표 구성원 수의 1에 반을 더한 값으로 반내림됩니다. 데이터가 포함된 voting 멤버 수가 투표 과반수보다 많지 않으면 기본 Write Concern는 {w:1}입니다.

이외 다른 모든 시나리오에서 기본 Write Concern는 {w: "majority" } 입니다.

구체적으로 MongoDB는 다음 공식을 사용하여 기본 Write Concern를 결정합니다.

if [ (#arbiters > 0) AND (#non-arbiters <= majority(#voting-nodes)) ]
    defaultWriteConcern = { w: 1 }
else
    defaultWriteConcern = { w: "majority" }


다음과 같은 리플리카 셋 구성과 시나리오가 있습니다.

Case | Non      | Arbiters | Voting  | Majority        | Implicit               |
     | Arbiters |          | Nodes   | of Voting Nodes | Default Write Concern  |
-----|----------|----------|---------|-----------------|------------------------|
  1  | 2        | 1        | 3       | 2               | { w: 1 }               |
  2  | 4        | 1        | 5       | 3               | { w: "majority" }      |


Case 1)
- 총 3개의 투표 노드에는 2명의 Non-Arbiters(일반 데이터 멤버)와 1명의 Arbiter 멤버가 있습니다.
- majority의 voting 노드수(1 + 3의 절반에서 반내림)는 2입니다.
- Non-Arbiters(일반 데이터 멤버)의 수는 2로 majority의 voting 노드(2)와 동일하므로 암시적 Write Concern 는 {w: 1 }이 됩니다.

Case 2)
- 총 5개의 투표 노드에는 4명의 Non-Arbiters(일반 데이터 멤버)와 1명의 Arbiter 멤버가 있습니다.
- majority의 voting 노드수(1 + 5의 절반에서 반내림)는 3입니다.
- Non-Arbiters(일반 데이터 멤버) 수가 4 임으로 majority의 voting 노드(3)보다 크므로 암시적 Write Concern 는 {w: "majority" } 이 됩니다.


이와 같이 리플리카 셋 멤버에 Arbiter가 있을 경우 Voting 노드수가 과반수가 되는지 여부에 따라서 암묵적 Write Concern 기본값이 설정됩니다.

writeConcern 를 다음과 같이 문장에서 설정할 수 있으며, 접속시에 설정할 수도 있습니다.

## 문장내에서 설정 
db.test_col.insertOne(
   { "item": "example", "qty": 100 },
   { writeConcern: { w : "majority", j : true } }
)

## 클라이언트 접속시 설정
mongodb://dbuser:dbpw@db0.example.com,db1.example.com,db2.example.com/?replicaSet=myRepl&w=majority&wtimeoutMS=5000

             

Read Concern

MongoDB 서버에서는 분산 처리를 기본 아키텍처로 선택하고 있기 때문에 쿼리를 통해 데이터를 읽는 과정에서도 일관성 또는 동기화를 고려해야 합니다.

Write Concern이 데이터 쓰기/변경에 대한 동기화라면 Read Concern는 읽기를 일관성 있게 유지할 수 있도록 하는 옵션 기능입니다.

Write Concern와Read Concern를 효과적으로 사용하면 적절한 수준의 일관성 및 가용성 보장을 조정할 수 있습니다.

예를 들어, 더 강력한 일관성 보장을 통해 조금 더 기다리거나, 일관성 요구 사항을 완화하여 더 높은 응답을 제공할 수 있습니다.

readConcern 옵션을 사용해 읽기 작업의 일관성과 격리수준, 가용성을 제어할 수 있습니다.

Read Concern 에는 5개의 옵션이 존재합니다.


local
쿼리는 데이터가 대부분의 레플리카 셋 구성원에 기록되었다는 보장 없이(즉, 롤백될 수도 있음) 쿼리가 실행되는 MongoDB서버가 가진 최신의 데이터를 반환합니다.

Read Concern의 기본값입니다.

Read concern "local"은 causally consistent sessions and transactions 에서 사용할 수 있습니다.


[참고] causally consistent session

작업이 논리적으로 이전 작업에 종속되는 경우 작업 간에 인과 관계가 있다고 할 수 있습니다. 예를 들어, 지정된 조건에 따라 모든 문서를 삭제하는 쓰기 작업과 삭제 작업을 확인하는 후속 읽기 작업은 인과관계가 있습니다.

MongoDB는 causally consistent session을 통해 인과 관계를 존중하는 순서로 인과관계 작업을 실행하고 클라이언트는 인과 관계와 일치하는 결과를 관찰합니다.

즉, 클라이언트의 작업이 시간적인 순서에 따라 일관성 있게 실행되도록 보장하는 세션입니다. 이는 데이터베이스에서 발생하는 여러 작업들 사이의 원인과 결과 관계를 유지하면서 일관성을 보장합니다. 이러한 causally consistent은 분산 데이터베이스 환경에서 중요한 역할을 합니다.

데이터가 여러 노드에 분산되어 있을 때, 각 노드는 독립적으로 작업을 처리하므로 작업의 순서를 보장하기 어렵습니다. 하지만 causally consistent session을 사용하면, 클라이언트는 작업의 순서를 보장받을 수 있습니다.

causally consistent session은 MongoDB 3.6 버전부터 도입되었으며, 세션 API를 통해 사용할 수 있습니다.

세션을 시작할 때 causally consistent session을 활성화하면, 해당 세션에서 실행되는 모든 작업은 원인적 일관성을 유지하게 됩니다.

causally consistent session 에 대한 더 자세한 내용은 별도의 포스팅으로 향후 정리하도록 하겠습니다.


available
기본적으로 local과 같이 동작하며, causally consistent session 및 트랜잭션(transactions)에서는 사용할 수 없습니다.

샤딩된 클러스터의 경우 Read Concern available는 다양한 Read Concern 중에서 가능한 가장 낮은 지연 시간을 제공합니다.

그러나 샤드된 컬렉션에서 읽을 때 orphaned(고아) document를 반환할 수 있으므로 일관성에 문제가 생길수도 있습니다.

샤드된 컬렉션에서 읽을 때 orphaned(고아) document가 반환되는 위험을 피하려면 다른 Read Concern(local 이나 available 외 다른) 의 사용을 고려해야 합니다.


majority
쿼리는 대다수의 레플리카 셋 구성원에 기록된 데이터를 반환합니다. majority 을 통해 읽은 Document에 대해서 오류가 발생하더라도 멤버 구성원 대다수에 기록이 되었기 때문에 데이터에 대해서는 지속성을 가지게 됩니다.

causally consistent session 또는 트랜잭션에서 사용할 수 있습니다.

레플리카 셋에서 majority Read Concern 으로 사용하려면 WiredTiger 스토리지 엔진을 사용해야 합니다.


linearizable
읽기 작업을 시작하기 전에 majority-acknowledged 완료된 쓰기에 대한 데이터만 반환합니다. 쿼리 실행 결과를 반환하기전에 레플리카 셋 전체에 데이터가 전파되기를 기다릴 수 있습니다.

읽기 작업 시작 후 레플리카 셋 멤버중 과반이 문제가 발생시 writeConcernMajorityJournalDefault가 기본 상태인 true인 경우 반환된 데이터는 지속성을 가집니다.

linearizable는 causally consistent session 또는 트랜잭션에서 사용할 수 없으며 프라이머리 멤버에만 사용할 수 있습니다.

majority 와 linearizable는 유사하지만 OpLog 전파되는 상태에 따라서 linearizable 이 majority 보다 더 최신의 데이터를 반환하게 되는데, 이유는 majority 와 linearizable의 작동 방식의 큰 차이가 있기 때문입니다.

majority 인 경우 MongoDB는 레플리카 셋 멤버들의 복제 상태를 참조하여 클라이언트에 반환할 데이터를 판단하게 되고 즉시 결과를 반환합니다. 즉 majority는 특정 시점의 데이터를 반환하게 되는 것입니다.

하지만 linearizable 인 경우 MongoDB는 자산이 가진 최신의 OpLog까지 모든 세컨더리에 전파될 때까지 가디렸다가 결과를 반환하게 됩니다.

그래서 OpLog 전파 상태에 따라서 linearizable 이 majority 보다 더 최신의 결과를 반환하게 됩니다. 하지만 linearizable는 majority 비해 쿼리의 응답 속도가 더 느려지게 됩니다.

이와 같이 linearizable는 프라이머리에서만 수행하게 되며, 수행시의 OpLog가 세컨더리 멤버까지 적용될 때까지 기다려야 하기 때문에 쿼리 응답시간이 많이 느려질 수 있습니다.


snapshot
Read Concern snapshot을 통한 쿼리는 최근 특정 단일 시점의 샤드 전체에 대해서 다수 커밋 데이터를 반환합니다. 다만 트랜잭션이 Write Concern majority로 커밋된 경우에만 보장을 제공합니다.

snapshot read concern은 multi-document 트랜잭션에 사용할 수 있으며, MongoDB 5.0부터는 multi-document 트랜잭션 외의 특정 읽기 작업에도 사용할 수 있습니다1.

트랜잭션이 causally consistent session 이 아니며, Write concern majority 으로 트랜잭션 커밋시, 트랜잭션은 과반이 커밋된 데이터의 스냅샷에서 읽는 것을 보장합니다.

트랜잭션이 causally consistent session 이며 Write concern majority 으로 트랜잭션 커밋시, 트랜잭션 시작 직전에 과반이 커밋된 데이터의 스냅샷에서 읽습니다.

multi-document 트랜잭션 외부에서 snapshot read concern은 프라이머리 멤버와 세컨더리 멤버에서 다음 읽기 작업에 사용할 수 있습니다. find, aggregate, distinct (unsharded collections에서만)

 

이번 포스팅에서는 MongoDB 레플리카 셋 환경에서의 쓰고 읽는 과정에서의 정합성 및 데이터 동기화 관련된 내용인 Write Concern 와 Read Concern 을 확인해보았으며, 글을 마무리하도록 하겠습니다.
          

Reference

Reference URL
mongodb/release-notes/5.0
mongodb/causal-consistency-read-write-concerns
mongodb/write-concern
mongodb/read-concern
mongodb/read-isolation-consistency-recency

Reference Book
• Real MongoDB


관련된 다른 글

 

 

 

 

             

0
글에 대한 당신의 생각을 기다립니다. 댓글 의견 주세요!x