Session.startTransaction()
정의
Session.startTransaction(<options>)
세션과 연결된 다중 문서 트랜잭션 을 시작합니다. 주어진 시간에 세션에 대해 열려 있는 트랜잭션 은 최대 하나만 있을 수 있습니다.
버전 4.2에서 변경 : MongoDB 4.2부터 샤딩된 클러스터와 복제본 세트 모두에서 다중 문서 트랜잭션을 사용할 수 있습니다.
중요
트랜잭션 내에서는 기존 collection에 대한 읽기 및 쓰기(CRUD) 작업만 지정할 수 있습니다. 예를 들어 다중 문서 트랜잭션에는 새 컬렉션을 생성하는 삽입 작업이 포함될 수 없습니다.
Session.startTransaction()
메서드는 다음 옵션이 있는 문서를 가져올 수 있습니다.{ readConcern: { level: <level>}, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } } 옵션설명readConcern
선택 사항입니다. 트랜잭션의 모든 연산에 대한 읽기 고려를 지정하는 문서로, 연산별 읽기 고려를 재정의합니다.
다음 읽기 고려 수준 중 하나를 지정할 수 있습니다.
"local"
및"majority"
읽기 고려에 대해 MongoDB는 때로 더 강력한 읽기 고려로 대체할 수 있습니다.writeConcern
호환성
이 메서드는 다음 환경에서 호스팅되는 배포에서 사용할 수 있습니다.
MongoDB Atlas: 클라우드에서의 MongoDB 배포를 위한 완전 관리형 서비스
MongoDB Enterprise: MongoDB의 구독 기반 자체 관리 버전
MongoDB Community: MongoDB의 소스 사용 가능 무료 자체 관리 버전
행동
트랜잭션 내에서 지원되는 작업
다중 문서 트랜잭션의 경우:
트랜잭션에서 컬렉션과 인덱스를 생성할 수 있습니다. 자세한 내용은 트랜잭션에서 컬렉션 및 인덱스 생성하기를 참조하세요
트랜잭션에 사용되는 컬렉션은 서로 다른 데이터베이스에 있을 수 있습니다.
참고
샤드 간 쓰기 트랜잭션에서는 새 컬렉션을 생성할 수 없습니다. 예를 들어, 하나의 샤드에서 기존 컬렉션에 쓰고 다른 샤드에서 암시적으로 새 컬렉션을 생성하는 경우, MongoDB는 동일한 트랜잭션에서 두 작업을 모두 수행할 수 없습니다.
제한된 컬렉션에는 쓸 (write) 수 없습니다.
고정 사이즈 컬렉션에서 읽을 때는 읽기 고려
"snapshot"
을 사용할 수 없습니다. (MongoDB 5.0부터 도입됨)config
,admin
또는local
데이터베이스의 컬렉션을 읽고 쓸 수 없습니다.system.*
컬렉션에 쓸 수 없습니다.explain
또는 이와 유사한 명령을 사용하여 지원되는 작업의 쿼리 계획을 반환할 수 없습니다.
트랜잭션 외부에서 생성된 커서의 경우 트랜잭션 내부에서
getMore
을(를) 호출할 수 없습니다.트랜잭션에서 생성된 커서의 경우 트랜잭션 외부에서
getMore
를 호출할 수 없습니다.
명령을 트랜잭션 의
killCursors
첫 번째 작업으로 지정할 수 없습니다.또한 트랜잭션 내에서
killCursors
명령을 실행 하면 서버 가 지정된 커서를 즉시 중지합니다. 트랜잭션 이 커밋 될 때까지 기다리지 않습니다.
메서드 | 명령 | 참고 |
---|---|---|
비샤드형 컬렉션에서 사용할 수 있습니다. For sharded collections, use the aggregation pipeline with the
$group stage. See Distinct Operation. | ||
존재하지 않는 컬렉션에서 자세한 내용은 관리 작업을 참조하세요. | ||
존재하지 않는 컬렉션에서 실행하면 컬렉션이 암시적으로 생성됩니다. 자세한 내용은 관리 작업을 참조하세요. | ||
존재하지 않는 컬렉션에서 실행하면 컬렉션이 암시적으로 생성됩니다. 자세한 내용은 관리 작업을 참조하세요. | ||
존재하지 않는 컬렉션에서 실행하면 컬렉션이 암시적으로 생성됩니다. 자세한 내용은 관리 작업을 참조하세요. |
컬렉션이나 인덱스 생성 또는 제거 등 데이터베이스 카탈로그에 영향을 주는 작업은 다중 문서 트랜잭션에서 허용되지 않습니다. 예를 들어, 다중 문서 트랜잭션에는 새 컬렉션을 생성하는 삽입 작업이 포함될 수 없습니다. 제한된 작업을 참조하세요.
2} buildInfo
,hello
connectionStatus
, 과 같은 정보 명령(및 그 도우미 메서드) (및 해당 헬퍼 메서드)와 같은 정보 명령은 트랜잭션에서 허용되지만, 트랜잭션의 첫 번째 연산이 될 수는 없습니다.
읽기 설정
트랜잭션 은 읽기 설정 primary
를 지원합니다.
원자성
트랜잭션이 열려 있는 동안에는 트랜잭션 연산으로 인한 데이터 변경 사항이 트랜잭션 외부에 표시되지 않습니다.
트랜잭션이 커밋되면 트랜잭션에서 이루어진 모든 데이터 변경 사항이 저장되고 트랜잭션 외부에서 볼 수 있습니다. 즉, 트랜잭션은 다른 트랜잭션을 롤백하는 동안 다른 변경 사항을 커밋하지 않습니다.
트랜잭션이 커밋될 때까지 트랜잭션에서 변경된 데이터는 트랜잭션 외부에 표시되지 않습니다.
그러나 트랜잭션이 여러 샤드에 쓰기를 수행하는 경우, 모든 외부 읽기 작업이 커밋된 트랜잭션의 결과가 샤드 전체에 표시될 때까지 기다릴 필요는 없습니다. 예를 들어, 트랜잭션이 커밋되고 쓰기 1이 샤드 A에 표시되지만 쓰기 2가 샤드 B에 아직 표시되지 않는 경우, 읽기 고려
"local"
의 외부 읽기는 쓰기 2를 보지 않고 쓰기 1의 결과를 읽을 수 있습니다.트랜잭션이 중단되면 트랜잭션 쓰기로 인한 모든 데이터 변경 사항이 표시되지 않고 삭제되며 트랜잭션이 종료됩니다.
예시
hr
데이터베이스의 직원 기록이 변경될 때 reporting
데이터베이스의 events
collection이 hr
변경 사항과 동기화되도록 하려는 시나리오를 생각해 보겠습니다. 즉, 이러한 쓰기가 단일 트랜잭션으로 수행되어 두 작업이 모두 성공하거나 실패하도록 해야 합니다.
hr
데이터베이스의 employees
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : ObjectId("5af0776263426f87dd69319a"), "employee" : 3, "name" : { "title" : "Mr.", "name" : "Iba Ochs" }, "status" : "Active", "department" : "ABC" } { "_id" : ObjectId("5af0776263426f87dd693198"), "employee" : 1, "name" : { "title" : "Miss", "name" : "Ann Thrope" }, "status" : "Active", "department" : "ABC" } { "_id" : ObjectId("5af0776263426f87dd693199"), "employee" : 2, "name" : { "title" : "Mrs.", "name" : "Eppie Delta" }, "status" : "Active", "department" : "XYZ" }
reporting
데이터베이스의 events
컬렉션에는 다음 문서가 포함되어 있습니다.
{ "_id" : ObjectId("5af07daa051d92f02462644a"), "employee" : 1, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } } { "_id" : ObjectId("5af07daa051d92f02462644b"), "employee" : 2, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "XYZ", "old" : null } } { "_id" : ObjectId("5af07daa051d92f02462644c"), "employee" : 3, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }
다음 예에서는 트랜잭션을 열고 employees
상태에서 직원의 상태를 Inactive
로 업데이트하고 events
컬렉션에 해당 문서를 삽입하고 두 작업을 단일 트랜잭션으로 커밋합니다.
// Runs the txnFunc and retries if TransientTransactionError encountered function runTransactionWithRetry(txnFunc, session) { while (true) { try { txnFunc(session); // performs transaction break; } catch (error) { // If transient error, retry the whole transaction if (error?.errorLabels?.includes("TransientTransactionError") ) { print("TransientTransactionError, retrying transaction ..."); continue; } else { throw error; } } } } // Retries commit if UnknownTransactionCommitResult encountered function commitWithRetry(session) { while (true) { try { session.commitTransaction(); // Uses write concern set at transaction start. print("Transaction committed."); break; } catch (error) { // Can retry commit if (error?.errorLabels?.includes("UnknownTransactionCommitResult") ) { print("UnknownTransactionCommitResult, retrying commit operation ..."); continue; } else { print("Error during commit ..."); throw error; } } } } // Updates two collections in a transactions function updateEmployeeInfo(session) { employeesCollection = session.getDatabase("hr").employees; eventsCollection = session.getDatabase("reporting").events; session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } ); try{ employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } ); eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } ); } catch (error) { print("Caught exception during transaction, aborting."); session.abortTransaction(); throw error; } commitWithRetry(session); } // Start a session. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } ); try{ runTransactionWithRetry(updateEmployeeInfo, session); } catch (error) { // Do something with error } finally { session.endSession(); }