개요
이 튜토리얼에서는 원자성, 일관성, 격리 및 내구성(ACID) 보장 충족하는 다중 문서 트랜잭션을 실행 방법을 학습 수 있습니다.
모든 MongoDB Server 버전은 단일 문서 트랜잭션을 지원 트랜잭션 에서 문서 를 여러 번 업데이트할 때 ACID compliance 보장합니다. MongoDB Server v4.0 이상에서는 여러 문서, 컬렉션, 데이터베이스에서 ACID 호환 트랜잭션을 실행 수 있습니다.
튜토리얼
이 튜토리얼에서는 제품 주식 및 구매 데이터에 대한 다중 문서 트랜잭션을 실행하는 예시 애플리케이션 을 transactions 다운로드 방법을 보여줍니다. 이 튜토리얼에서는 예시 애플리케이션의 디렉토리 에 있는 다음 파일을 사용합니다.
Transactions.java:cart및product컬렉션에 액세스한 다음 두 컬렉션 모두에서 작업을 실행하여 맥주 구매를 반영합니다. 이 코드는 접근 방식을 비교하기 위한 트랜잭션 없이 ACID transaction 내에서 작동합니다.ChangeStreams.java:cart및product컬렉션의 데이터 변경 사항에 대한 정보를 반환합니다.models/Cart.java: POJO 클래스로, 컬렉션 의 문서 에 해당하는 쇼핑카트를cart나타냅니다.models/Product.java:product컬렉션 의 문서 에 해당하는 하나의 항목과 해당 주식 나타내는 POJO 클래스입니다.
전제 조건을 확인합니다.
이 튜토리얼을 시작하기 전에 다음 구성 요소가 준비되어 있는지 확인하십시오.
클러스터 가 구성된 MongoDB Atlas 계정. 클러스터 만드는 방법을 학습 보려면 MongoDB 시작하기 가이드 참조하세요.
Java 운전자 v5.0 이상.
Java 21 이상.
Maven v3.8.7 이상.
변경 스트림 시작하고 컬렉션을 구성합니다.
cart 및 product 컬렉션을 만들고 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데이터베이스 의 변경 사항을 모니터 .
샘플 데이터를 삽입하여 데이터 작업을 시작합니다.
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) }
트랜잭션 없이 첫 번째 작업을 실행합니다.
샘플 데이터를 삽입한 후 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) }
다중 문서 트랜잭션 내에서 두 번째 작업을 실행합니다.
초기 구매 후 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 내에서 실행 cart 및 product 업데이트는 원자적입니다.
Cart 탭 선택하여 Alice의 쇼핑 카트를 나타내는 업데이트된 cart 문서 확인하고, Product 탭 선택하여 맥주 재고를 나타내는 업데이트된 product 문서 확인합니다.
{ "_id" : "Alice", "items" : [ { "price" : NumberDecimal("3"), "productId" : "beer", "quantity" : NumberInt(4) } ] }
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(1) }
트랜잭션 내에서 실패한 작업을 실행합니다.
마지막으로 Alice는 자신의 장바구니에 맥주를 두 잔 더 넣으려고 시도합니다. Transactions.java 파일 트랜잭션 통해 aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드를 다시 호출하여 이 세 번째 작업을 실행 .
그러나 주식 가 단 하나뿐이므로 이 작업은 성공하지 못합니다. ChangeStreams.java 파일 에 구성된 JSON schema product 컬렉션의 stock 값이 0보다 작을 수 없도록 하므로 현재 값에서 2 를 빼려고 하면 오류가 발생합니다. aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() 메서드는 트랜잭션 을 롤백합니다.
변경 스트림 출력을 검토합니다.
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가 다음에 맥주 두 개를 구매하면
cart및product컬렉션 도 모두 업데이트됩니다. 이러한 작업은 다중 문서 트랜잭션 에서 원자적으로 실행 되므로Timestamp값이 동일합니다.
이 튜토리얼을 완료하면 주식 관리 데이터를 업데이트하는 애플리케이션 만들 수 있습니다. 애플리케이션 다중 문서 ACID transaction 유무에 관계없이 이러한 업데이트 작업을 수행하여 두 결과를 비교합니다.
추가 정보
전체 예시 애플리케이션 보려면 java-quick-start GitHub 리포지토리 의 transaction 폴더를 참조하세요.