View a markdown version of this page

DynamoDB 트랜잭션을 사용한 비관적 잠금 - Amazon DynamoDB

DynamoDB 트랜잭션을 사용한 비관적 잠금

DynamoDB 트랜잭션은 그룹화된 작업에 대한 양자택일 접근 방식을 제공합니다. TransactWriteItems를 사용하면 DynamoDB가 트랜잭션의 모든 항목을 모니터링합니다. 트랜잭션 중에 다른 작업에 의해 항목이 수정되면 전체 트랜잭션이 취소되고 DynamoDB가 TransactionCanceledException을 반환합니다. 이 동작은 충돌하는 동시 수정이 사후에 감지되지 않기 때문에 비관적 동시성 제어의 한 형태를 제공합니다.

잠금에 트랜잭션을 사용해야 하는 경우

트랜잭션은 다음과 같은 경우에 적합합니다.

  • 동일한 테이블 내에서 또는 여러 테이블 간에 여러 항목을 원자적으로 업데이트해야 합니다.

  • 비즈니스 로직에는 모든 변경 사항이 성공하거나 적용되지 않는 모든 또는 양자택일 의미 체계가 필요합니다.

일반적인 예로는 계정 간 자금 이체, 인벤토리 및 주문 테이블을 모두 업데이트하는 주문, 게임 내 플레이어 간 항목 교환 등이 있습니다.

단점

더 높은 쓰기 비용

최대 1KB 항목의 경우 트랜잭션은 항목당 2WCU(준비할 항목 하나, 커밋할 항목 하나)를 소비하는 반면 표준 쓰기의 경우 1WCU를 소비합니다.

항목 제한

단일 트랜잭션에는 하나 이상의 테이블에 걸쳐 최대 100개의 작업이 포함될 수 있습니다.

충돌 민감도

트랜잭션의 항목이 다른 작업에 의해 수정되면 전체 트랜잭션이 실패합니다. 경쟁이 많은 시나리오에서는 취소가 자주 발생할 수 있습니다.

구현

다음 예제에서는 TransactWriteItems를 사용하여 두 항목 간에 인벤토리를 원자적으로 전송합니다. 다른 프로세스가 트랜잭션 중에 두 항목을 수정하면 전체 작업이 롤백됩니다.

import boto3 client = boto3.client('dynamodb') def transfer_inventory(source_id, target_id, quantity): try: client.transact_write_items( TransactItems=[ { 'Update': { 'TableName': 'Inventory', 'Key': {'ItemID': {'S': source_id}}, 'UpdateExpression': 'SET QuantityLeft = QuantityLeft - :qty', 'ConditionExpression': 'QuantityLeft >= :qty', 'ExpressionAttributeValues': { ':qty': {'N': str(quantity)} } } }, { 'Update': { 'TableName': 'Inventory', 'Key': {'ItemID': {'S': target_id}}, 'UpdateExpression': 'SET QuantityLeft = QuantityLeft + :qty', 'ExpressionAttributeValues': { ':qty': {'N': str(quantity)} } } } ] ) return True except client.exceptions.TransactionCanceledException as e: print(f"Transaction canceled: {e}") return False

이 예제에서 조건 표현식은 인벤토리가 충분한지 확인하지만 버전 속성은 필요하지 않습니다. DynamoDB는 준비 단계와 커밋 단계 사이의 다른 작업에 의해 트랜잭션의 항목이 수정된 경우 트랜잭션을 자동으로 취소합니다. 이는 비관적 동시성 제어를 제공합니다. 트랜잭션 자체가 충돌하는 동시 수정을 방지합니다.

참고

버전 확인을 조건 표현식으로 추가하여 트랜잭션을 낙관적 잠금과 결합할 수 있습니다. 이는 추가 보호 계층을 제공하지만 트랜잭션이 충돌을 감지하는 데 필요하지 않습니다.

자세한 내용은 DynamoDB Transactions를 사용하여 복잡한 워크플로 관리 섹션을 참조하세요.