성장, 그리고 노력

부족하더라도 어제보다 더 잘해지자. 노력은 절대 배신하지 않는다.

데이터베이스/MongoDB

[MongoDB] Update ... 대치? 연산자? 뭐가 더 좋을까

제이콥(JACOB) 2020. 6. 27. 12:25

 

DB를 업데이트하는 것은 기존의 도큐먼트에 "(덮어) 쓰기"를 하는 것이다. 업데이트를 하는 방법에는 두 가지가 있으며 제목에 대한 이야기를 하기 전에 간단하게 "대치에 의한 update"와 "연산자에 의한 update"를 먼저 짚어보자.

 

대치에 의한 Update

특정 유저에 상품의 좋아요 카운트를 증가시키는 경우에 대해서 예를 들어보자.

const product_id = ObjectId("5da78973835b2f1c75347a83");
const doc = db.products.findOne({_id: product_id});
document['total_like'] += 1;

db.products.update({_id: product_id}, doc);

먼저 products에 _id를 가지고 먼저 쿼리했다. 그다음 가져온 도큐먼트(doc)에서 total_like 필드에 1을 더한 후 업데이트를 했다. 여기서 기억해야 할 점은 업데이트 연산이 전체 도큐먼트를 대치한다는 점이다. 이것이 업데이트 연산이 가장 먼저 페치 되어야 하는 이유이며, 여러 사용자가 동일한 도큐먼트를 업데이트하는 경우 마지막 쓰기 연산이 수행된 내용으로 저장된다. 

 

연산자에 의한 update

연산자 업데이트를 통해서 만든 위와 같은 예제이다.

const product_id = ObjectId("5da78973835b2f1c75347a83");
db.products.update({_id: product_id}, {$inc: {"total_like": 1}});

 

그래서 뭐가 좋은데?

답은 뭐 다들 알고 있는 것이다. 항상 좋은 건 없고 그때그때 다르다. 우리가 이것들의 사용할때 고려할 점은 4가지 정도가 있다.

 

  • 어떤 것이 더 직관적인가?
  • 어떤 것이 더 좋은 성능을 가질까?
  • 다중 스레드가 동시에 업데이트를 수행하면 어떤 일이 발생할까? 원자적 행위(atomic action)이 필요한가?
  • 업데이트는 서로 독립적인가?

 대치에 의한 업데이트는 일반적인 방식이다. 사용자 정보를 수정하는 폼을 생각하면 쉽다. 사용자가 제출한 폼의 데이터가 일단 일정이 되고 나면 MongoDB로 전달될 수 있다. 업데이트를 일반화하는 MongoDB 객체 매퍼를 작성한다면 더 좋은 선택이 될 수 있다. 

 

 반면 연산자에 의한 업데이트는 좀 더 나은 성능을 가진다. 수정할 도큐먼트를 가져오기 위해 서버에 도큐먼트 요청을 할 필요가 없으며, 업데이트를 지정하는 도큐먼트의 크기가 일반적으로 작다. 또한 이 방식은 원자적 업데이트를 하는데 적합하다. 예를 들어 위와 같은 카운트를 증가시키는 예제에서는 대치에 의한 업데이트는 좋은 방법이 아니다. 만약 위 도큐먼트를 읽고 쓰는 사이에 변경이 발생한다면 어떻게 될까? 원자적으로 수행할 수 있는 유일한 방법은 일정의 낙관적인 잠금을 통해서다. 하지만 위에 있는 연산자 업데이트는 $inc를 사용해서 카운터를 자동으로 증가시킬 수 있다. 대량의 동시적 업데이트일지라도 각각의 $inc는 고립적으로 적용되어 증가되거나 증가되지 않거나 둘 중의 한 가지만 가능하다. 

 

또 한 가지 기억할 점은 위 두 방법을 섞어서 업데이트하면 일관성이 깨질 수 있기 때문에 안된다는 점이다. 물론 이를 낙관적인 잠금을 통해 해결 할 수도 있겠지만, 그것보다는 모두 연산자에 의한 업데이트로 통일하는 것이 훨씬 간단하다. 

 

cf) Optimisic locking vs Pessimistic locking

 한글로 하자면 낙관적 잠금과 비관적 잠금이라고 번역할 수 있다. 낙관적(비관적) 동시성 제어라고도 하는데, 낙관적 잠금은 레코드를 잠그지 않고도 업데이트 연산이 제대로 수행되는 것을 보장하기 위한 기술이다. 레코드를 읽고 버전 번호, 날짜, 타임스탬프, 체크섬, 해서 등을 기록하고 레코드를 다시 쓰기 전에 버전이 변경되지 않았는지 확인하는 전략이다. 만약 레코드가 기록된 부분과 다르다면 트랜잭션을 중단하고 사용자가 다시 시작할 수 있다. 위키같이 데이터베이스에 대한 연결을 반드시 유지할 필요가 없는 대용량 시스템 및 3계층 아키텍처에 적합하다. 만약 많은 충돌이 예상된다면 이 전략을 피하는 것이 좋다.

 반면 비관적 잠금의 경우 레코드가 트랜잭션에서 처음 엑세스할 때부터 트랜잭션이 완료될 때까지 레코드를 잠가서 그 시간 동안 다른 트랜잭션이 액세스 할 수 없게 한다. 낙관적 잠금보다 무결성이 훨씬 뛰어나지만, 교착 상태를 피하려면 설계를 잘해야 한다. 많은 충돌이 예상된다면 이 전략을 선택하여, 동기화를 위반하는 트랜잭션을 차단하는 것이 좋다.

 

 

 

Optimistic vs. Pessimistic locking

I understand the differences between optimistic and pessimistic locking. Now could someone explain to me when I would use either one in general? And does the answer to this question change depend...

stackoverflow.com

 

 

Atomicity and Transactions — MongoDB Manual

When a single write operation (e.g. db.collection.updateMany()) modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic. When performing multi-document write operations, whether through a single

docs.mongodb.com

 

반응형

'데이터베이스 > MongoDB' 카테고리의 다른 글

MongoDB TTL 컬렉션  (0) 2020.06.06
MongoDB 기초  (0) 2020.05.24