인프런 커뮤니티 질문&답변

손봉호님의 프로필 이미지

작성한 질문수

MongoDB를 활용하여, 200억건 이상의 데이터 파이프라인 작성법

여러 thread에서 Upsert를 실행하면 괜찮을까요?

24.06.11 16:19 작성

·

120

0

MYSQL 강의에서는 ON DUPLICATE KEY UPDATE 를 쓰면 여러 쓰레드에서 호출시, lock이 걸릴 수 있다고 강의에서 보았습니다

혹시 몽고DB는 여러 thread에서 upsert 시도시, lock이슈가 있을까요>?

답변 1

0

July님의 프로필 이미지
July
지식공유자

2024. 06. 11. 17:24

안녕하세요 먼저 질문 주셔서 감사합니다!! 😆

먼저 여러 쓰레드가 접근하는 방식에 따라서 다릅니다.

만약 세션이라는 형태로 여러개의 Tx를 하나로 묶어서 보내는 경우에 있다면 당연하게도 발생을 합니다.

  • 이를 Mutex라고 합니다.

  • Java에서의 JPA는 기본적으로 Session을 형성해서 전송을 하기 떄문에, 이러한 문제가 많이 발생하는 것으로 알고 있습니다.

    ON DUP UPDATE하는 쿼리도 기본 베이스는 세션을 사용하는 경우를 가정 합니다.

  • 세션을 사용하지 않고 발생을 하게 된다면, 이는 Tx의 격리 상태 또는 MySQL 엔진 문제로 보아야 합니다.

  • 기본적으로 MongoDB나 MySQL의 여러 쓰레드를 보장하기 위해서 Lock이라는 개념을 사용하기 떄문이죠

 

혹시 질문에 대한 답이 되었을까요?? 추가적인 궁금증이 있다면 질문 남겨주시면 감사하겠습니다!! 😆

손봉호님의 프로필 이미지
손봉호
질문자

2024. 06. 11. 17:35



안녕하세요 제가 잘 이해가 안되서 다시 질문드립니다

"세션이라는 형태로 여러개의 Tx를 하나로 묶어서 보내는 경우"라는 뜻의 의미가 1개의 connection을 말씀하시는 걸까요???
질문1) 1개의 커넥션에 아래와 같이 보내면 문제라는 말씀으로 이해하면 될까요?
질문2) 그렇다면 mongoDb는 커넥션 연결을 재사용하기에 문제가 없다는 말씀으로 이해하면 될지요??



```c#
// Mongo 유
var mongoDb = MongoDbConnectorHelper.Instance;

var databaseName = $"app";

var database = mongoDb.GetDatabase(databaseName);

var collectionName = $"test";

var collection = database.GetCollection<ChatMessage>(collectionName);

using (var session = mongoDb.StartSession())

{

session.StartTransaction(); // 트랜잭션 시작

try

{

// 읽은 상태 갱신

{

collection.UpdateOne(session, filter, update);

}

session.CommitTransaction(); // 트랜잭션 커밋

return true;

}

catch (MongoConnectionException mEx)

{

session.AbortTransaction();

MongoDbConnectorHelper.HandleMongoConnectionException(mEx);

_logger.Error("reconnect to MongoDB.");

return false;

}

catch (Exception ex)

{

session.AbortTransaction();

_logger.Error("faild to UpdateReadStatusAsync.", ex);

return false;

}

}
``````````````
```c#
// MYSQL예제
var mySqlConnection = new MySqlConnection(ConnectionString);

using (var db = await mySqlConnection.OpenAsync())

{

if (db == null)

{

return new();

}

var query = $"select * from test where a=@a limit 1;";

var reuslt = await db.QueryAsync(query, new { a = 11 });

var tblCharacter = reuslt?.FirstOrDefault() ?? null;

return tblCharacter != null ? tblCharacter : new TblMember { user_seq = 0 };

}
```

ps.
MySQl은 아래 상황에서 Lock문제가 발생한다고 공부했습니다.
A.

INSERT INTO test (pk, name) VALUES (1, ‘1), (2, ‘b’) ON DUPLICATE KEY UPDATE name = VALUES(name)

B.

INSERT INTO test (pk, name) VALUES (2, ‘1), (2, ‘b’) ON DUPLICATE KEY UPDATE name = VALUES(name)

A에서 pk 1 raw에 대해서 lock을 획득을한다.

B에서 pk 2 raw에 대해서 lock을 획득을한다.

A에서 pk 2 raw에 대해서 lock을 획득을한다. (B가 가지고있는 상황)

B에서 pk 1 raw에 대해서 lock을 획득을한다.(A가 가지고있는 상황)


July님의 프로필 이미지
July
지식공유자

2024. 06. 11. 17:49

빠른 답변 감사합니다!!
일단 제가 C# 코드에 익숙하지가 않아서, 코드로만 확인을 하기에는 무리가 있지만 다음과 같은 문서를 확인한 기준으로 답변을 드리겠습니다.

  • https://www.mongodb.com/ko-kr/docs/drivers/csharp/v2.20/fundamentals/transactions/


    해당 문서에서는 StartTransaction 은 새 트랜잭션을 시작하며, 세션에 대해 이미 진행중인 트랜잭션이 있을 시 예외가 발생을 한다고 나와 있습니다.

     


    이를 쉽게 설명하면 중복된 세션에 대한 도큐먼트 접근이나 Raw 접근은 허용하지 않는것을 말합니다.

  • 이것이 Mutex 개념 입니다.

 

그러니 1번과 같이 전송을 하면 세션을 활용하는것이기 때문에, 다른 외부 모듈이나 DB작업에 대해서 예외 상황이 발생을 하게 될 겁니다.

 

2번도 동일합니다.

DB는 HDD 데이터베이스이며, 세션을 활용하여 동작하는 방식이 기본적으로 기존 데이터를 복사하고, 값을 변경하고 업데이트 하는 방식입니다.

이 떄 복사한 데이터를 업데이트 하는 과정에서 기존 복사했을 떄 데이터의 값과 다르다면 예외가 발생을 한다고 보시면 됩니다.

  • 이게 세션의 동작 방식입니다.

 

세션이라는 말이 꽤나 헷갈리시는 경우가 많으세요. 실제 실무에서도 세션이라고 언급을 하면 헷갈리신다고 하시는 분들도 계시고요. 이는 제가 사용하기 편한 용어일뿐 좀 더 익숙한 용어로 사용하셔도 무방합니다.

 

그래서 동시성있는 프로그램에서 세션의 형태를 사용하는 것은 매우 위험합니다.