Docs Menu
Docs Home
/ /

튜토리얼: 다중 문서 ACID 트랜잭션 실행

이 튜토리얼에서는 원자성, 일관성, 격리 및 내구성(ACID) 보장 충족하는 다중 문서 트랜잭션을 실행 방법을 학습 수 있습니다.

모든 MongoDB Server 버전은 단일 문서 트랜잭션을 지원 트랜잭션 에서 문서 를 여러 번 업데이트할 때 ACID compliance 보장합니다. MongoDB Server v4.0 이상에서는 여러 문서, 컬렉션, 데이터베이스에서 ACID 호환 트랜잭션을 실행 수 있습니다.

이 튜토리얼에서는 제품 주식 및 구매 데이터에 대한 다중 문서 트랜잭션을 실행하는 예시 애플리케이션 을 transactions 다운로드 방법을 보여줍니다. 이 튜토리얼에서는 예시 애플리케이션의 디렉토리 에 있는 다음 파일을 사용합니다.

  • Transactions.java: cartproduct 컬렉션에 액세스한 다음 두 컬렉션 모두에서 작업을 실행하여 맥주 구매를 반영합니다. 이 코드는 접근 방식을 비교하기 위한 트랜잭션 없이 ACID transaction 내에서 작동합니다.

  • ChangeStreams.java: cartproduct 컬렉션의 데이터 변경 사항에 대한 정보를 반환합니다.

  • models/Cart.java: POJO 클래스로, 컬렉션 의 문서 에 해당하는 쇼핑카트를 cart 나타냅니다.

  • models/Product.java: product 컬렉션 의 문서 에 해당하는 하나의 항목과 해당 주식 나타내는 POJO 클래스입니다.

1

이 튜토리얼을 시작하기 전에 다음 구성 요소가 준비되어 있는지 확인하십시오.

  • 클러스터 가 구성된 MongoDB Atlas 계정. 클러스터 만드는 방법을 학습 보려면 MongoDB 시작하기 가이드 참조하세요.

  • Java 운전자 v5.0 이상.

  • Java 21 이상.

  • Maven v3.8.7 이상.

2

터미널에서 다음 명령을 실행 하여 MongoDB 개발자 GitHub 리포지토리 에서 샘플 애플리케이션 복제합니다.

git clone git@github.com:mongodb-developer/java-quick-start.git

이 리포지토리 이 튜토리얼의 파일을 저장하는 transactions 폴더가 포함되어 있습니다.

3

cartproduct 컬렉션을 만들고 JSON schema 구성하려면 ChangeStreams.java 파일 실행 .java-quick-start 디렉토리 로 이동하여 다음 명령을 실행 .

mvn compile exec:java \
-Dexec.mainClass="com.mongodb.quickstart.transactions.ChangeStreams" \
-Dmongodb.uri="<connection URI>"

<connection URI> 자리 표시자를 클러스터의 연결 URI로 바꿉니다.

이 파일은 다음과 같은 작업을 수행합니다.

  • test 데이터베이스 에 cart 컬렉션 만듭니다.

  • test 데이터베이스 에 product 컬렉션 만듭니다.

  • 문서 필드에 데이터 유형 및 값 제약 조건을 설정하는 product 컬렉션 에 JSON schema 적용합니다. 이 스키마 stock 필드 값이 0 이상으로 유지되도록 하므로, 재고가 없는 품목을 구매하려고 하는 모든 트랜잭션 오류가 발생하고 성공하지 못합니다.

  • 변경 스트림 열어 test 데이터베이스 의 변경 사항을 모니터 .

4

Alice는 맥주를 사고 싶어 하는 샘플 고객입니다.Transactions.java 파일 구매를 반영하기 위해 데이터베이스 작업을 실행합니다. 이 프로그램을 시작하려면 두 번째 터미널 창 열고 프로젝트의 루트 디렉토리 에서 다음 코드를 실행 .

mvn compile exec:java \
-Dexec.mainClass="com.mongodb.quickstart.transactions.Transactions" \
-Dmongodb.uri="<connection URI>"

<connection URI> 자리 표시자를 클러스터의 연결 URI로 바꿉니다.

이 파일 맥주 재고를 나타내는 product 컬렉션 에 문서 삽입하고 해당 stock 값을 5로 설정합니다. 이 문서 다음 데이터가 저장됩니다.

{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(5) }
5

샘플 데이터를 삽입한 후 Transactions.java 파일 첫 번째 업데이트 작업을 실행하여 Alice가 맥주 두 개를 구매했음을 나타냅니다. 이 코드는 트랜잭션 시작하지 않고 aliceWantsTwoBeers()removingBeersFromStock() 메서드를 호출합니다. 이러한 메서드에는 다음과 같은 정의가 있습니다.

private static void aliceWantsTwoBeers() {
System.out.println("Alice adds 2 beers in her cart.");
cartCollection.insertOne(new Cart("Alice", List.of(new Cart.Item(BEER_ID, 2, BEER_PRICE))));
}
private static void removingBeersFromStock() {
System.out.println("Trying to update beer stock : -2 beers.");
try {
productCollection.updateOne(filterId, decrementTwoBeers);
} catch (MongoException e) {
System.out.println("######## MongoException ########");
System.out.println("##### STOCK CANNOT BE NEGATIVE #####");
throw e;
}
}

aliceWantsTwoBeers() 메서드는 구매를 나타내는 cart 컬렉션 에 문서 삽입하여 Alice의 쇼핑카트에 맥주 두 개를 추가합니다. 그런 다음 removingBeersFromStock() 메서드는 product 컬렉션 업데이트하여 변경 사항을 반영하고 stock의 맥주 수를 줄입니다.

Cart 탭 선택하여 Alice의 쇼핑카트를 나타내는 새 cart 문서 보고, Product 탭 선택하여 작업 후 맥주 재고를 나타내는 product 문서 확인합니다.

{
"_id" : "Alice",
"items" : [
{
"price" : NumberDecimal("3"),
"productId" : "beer",
"quantity" : NumberInt(2)
}
]
}
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(3) }
6

초기 구매 후 Alice는 그녀의 카트에 맥주 두 개를 더 추가합니다. Transactions.java 파일 트랜잭션 통해 aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드를 호출하고 MongoClient 를 인수로 전달하여 이 두 번째 작업을 실행 합니다. 이 메서드의 정의는 다음과 같습니다.

private static void aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(MongoClient client) {
ClientSession session = client.startSession();
try {
session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build());
aliceWantsTwoExtraBeers(session);
sleep();
removingBeerFromStock(session);
session.commitTransaction();
} catch (MongoException e) {
session.abortTransaction();
System.out.println("####### ROLLBACK TRANSACTION #######");
} finally {
session.close();
System.out.println("####################################\n");
printDatabaseState();
}
}

aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드는 세션을 시작한 다음 트랜잭션 시작합니다. 트랜잭션 내에서 이 코드는 헬퍼 메서드를 호출하여 다음 조치를 수행합니다.

  • Alice의 장바구니를 나타내는 cart 컬렉션 에서 문서 찾습니다.

  • 문서의 items.quantity 값을 2만큼 업데이트합니다.

  • 변경 사항을 반영하도록 맥주 주식 나타내는 product 컬렉션 의 문서 업데이트합니다.

다중 문서 ACID transaction 내에서 실행 cartproduct 업데이트는 원자적입니다.

Cart 탭 선택하여 Alice의 쇼핑 카트를 나타내는 업데이트된 cart 문서 확인하고, Product 탭 선택하여 맥주 재고를 나타내는 업데이트된 product 문서 확인합니다.

{
"_id" : "Alice",
"items" : [
{
"price" : NumberDecimal("3"),
"productId" : "beer",
"quantity" : NumberInt(4)
}
]
}
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(1) }
7

마지막으로 Alice는 자신의 장바구니에 맥주를 두 잔 더 넣으려고 시도합니다. Transactions.java 파일 트랜잭션 통해 aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드를 다시 호출하여 이 세 번째 작업을 실행 .

그러나 주식 가 단 하나뿐이므로 이 작업은 성공하지 못합니다. ChangeStreams.java 파일 에 구성된 JSON schema product 컬렉션의 stock 값이 0보다 작을 수 없도록 하므로 현재 값에서 2 를 빼려고 하면 오류가 발생합니다. aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드는 트랜잭션 을 롤백합니다.

8

Transactions.java 실행 완료되면 ChangeStreams.java 파일 출력은 다음과 유사합니다.

Dropping the 'test' database.
Creating the 'cart' collection.
Creating the 'product' collection with a JSON Schema.
Watching the collections in the DB test...
Timestamp{value=7304460075832180737, seconds=1700702141, inc=1} => Document{{_id=beer, price=3, stock=5}}
Timestamp{value=7304460075832180738, seconds=1700702141, inc=2} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=2}}]}}
Timestamp{value=7304460080127148033, seconds=1700702142, inc=1} => Document{{_id=beer, price=3, stock=3}}
Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=4}}]}}
Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=beer, price=3, stock=1}}

변경 스트림 컬렉션 구성 및 다음 작업에 대한 정보를 출력합니다.

  • 삽입 작업은 맥주를 나타내는 product 컬렉션 에 문서 추가합니다.

  • Alice가 처음으로 맥주 두 개를 구매했는데, 여기에는 cart 컬렉션 업데이트 작업과 product 컬렉션 업데이트 작업, 이렇게 두 가지 작업이 포함됩니다. 이러한 작업은 트랜잭션 내에서 실행 되지 않습니다. 작업은 원자적으로 실행 되지 않으므로 Timestamp 값이 다릅니다.

  • Alice가 다음에 맥주 두 개를 구매하면 cartproduct 컬렉션 도 모두 업데이트됩니다. 이러한 작업은 다중 문서 트랜잭션 에서 원자적으로 실행 되므로 Timestamp 값이 동일합니다.

이 튜토리얼을 완료하면 주식 관리 데이터를 업데이트하는 애플리케이션 만들 수 있습니다. 애플리케이션 다중 문서 ACID transaction 유무에 관계없이 이러한 업데이트 작업을 수행하여 두 결과를 비교합니다.

전체 예시 애플리케이션 보려면 java-quick-start GitHub 리포지토리 의 transaction 폴더를 참조하세요.

트랜잭션에 대해 자세히 학습 트랜잭션 가이드 참조하세요.

돌아가기

트랜잭션