BLOG
대부분의 관계형 데이터베이스 시스템 및 일부 비 관계형 데이터베이스 시스템에는 고유 키 또는 고유 제한 조건으로 알려진 구성이 있습니다. 이는 열 또는 필드의 모든 값이 행에서 고유해야 함을 뜻합니다.
예를 들어 User 테이블이 있는 경우 각 사용자를 고유하게 식별하는 기본 키로 UUID가있을 수 있지만 사용자 이름 및 이메일 필드(DynamoDB 용어의 “속성”)도 있을 수 있습니다. 이 사용 사례는 AWS Summit 2018 당시 DynamoDB Transaction DAT374 세션에서 언급된 적 있습니다.
아마존 DynamoDB에서는 기본 키가 파티션 키(테이블을 위한 키가 정렬되지 않은 경우) , 파티션의 조합 또는 정렬 키 중에 있습니다. 기본 키는 테이블 내에서 고유해야 합니다. 그러나 DynamoDB에는 기본 키가 아닌 속성의 고유성을 보장하기 위한 기본 제공 메커니즘이 없습니다.
오늘 메가존 테크블로그에선 애플리케이션 측에서 이러한 종류의 고유성을 구현하는 데 사용되는 패턴에 대해 설명해 드릴 예정입니다. 또한 단일 테이블 스키마 디자인에서 해당 패턴을 사용할 때 항목을 작성, 업데이트 및 삭제하는 방법에 대한 예제를 보여드리겠습니다.
솔루션 개요
앞의 예제를 사용하여 테이블에 다음과 같은 속성이 있다고 가정해보겠습니다.
- pk (UUID로 저장된 기본 키)
- 사용자 이름
- 이메일
- fullName
- 전화 번호
UUID , userName 및 이메일 속성은 고유해야하지만 fullName과 PHONENUMBER은 고유하지 않습니다. 여러 사람이 동일한 집 전화 번호를 공유 할 수 있기 때문입니다. 이에 대한 샘플은 다음과 같습니다.
Pk | 사용자 이름 | 이메일 | fullName | 전화 번호 |
b201c1f2-238e-461f-88e6-0e606fbc3c51 | 테이블 | bobby.tables@gmail.com | 바비 테이블 | + 1-202-555-0124 |
8ec436a8-97e6-4e72-aec2-b47668e96a94 | jsmith | johnsmith@yahoo.com | 존 스미스 | + 1-404-555-9325 |
eed78b78-29f9-4893-a432-4c4f50b0d1c4 | phonork | pphonork | 피터 포노르 쿠스 | + 1-805-555-0820 |
DynamoDB는 이미 pk 속성이 고유하다는 것을 보장하므로 userName 및 email 속성도 고유 하도록 메커니즘이 필요합니다.
이렇게 하려면 pk 속성을 항목의 속성 이름 및 값으로 설정하고 해시 부호로 구분 하여 동일한 테이블에 추가 항목을 삽입해야 합니다. 새 테이블에 대한 예는 다음과 같습니다.
Pk | 사용자 이름 | 이메일 | fullName | 전화 번호 |
b201c1f2-238e-461f-88e6-0e606fbc3c51 | 테이블 | bobby.tables@gmail.com | 바비 테이블 | + 1-202-555-0124 |
userName # btables | ||||
email#bobby.tables@gmail.com | ||||
8ec436a8-97e6-4e72-aec2-b47668e96a94 | jsmith | johnsmith@yahoo.com | 존 스미스 | + 1-404-555-9325 |
userName # jsmith | ||||
email#johnsmith@yahoo.com | ||||
eed78b78-29f9-4893-a432-4c4f50b0d1c4 | phonork | pphonork@calpoly.edu | 피터 포노르 쿠스 | + 1-805-555-0820 |
userName # phonork | ||||
email#pphonork@calpoly.edu |
새 항목을 테이블에 삽입할 때마다 다른 두 항목도 삽입해야 합니다. 이는 userName 및 email 속성의 고유성을 보장 합니다. 마찬가지로 항목을 삭제하는 경우 다른 두 개의 해당 항목을 삭제해야 합니다.
마지막으로 고유 속성 중 하나를 수정하는 경우(예 : 사용자가 자신의 계정에서 이메일 주소를 변경하려는 경우) 관련 고유 항목도 업데이트해야 합니다. 이러한 모든 수정 사항은 DynamoDB 트랜잭션에서 함께 바인딩되어 모두 성공하거나 함께 실패해야 합니다.
명령 줄 예제
다음으로 이 디자인을 구현하기 위한 몇 가지 명령 줄 예제를 살펴보겠습니다. 이러한 CLI 예제는 원하는 프로그래밍 언어에 맞게 DynamoDB SDK로 쉽게 이식 할 수 있습니다.
비어있는 사용자 테이블이 있다고 가정해보겠습니다. 트랜잭션의 일부로 세 개의 행을 삽입하여 첫 번째 사용자를 등록하고 트랜잭션 식별자를 사용하여 dem 등성을 보장합니다. 이 트랜잭션 식별자 (클라이언트 요청 토큰)를 사용하면 응용 프로그램이 다시 시작되거나 재개된 경우에도 동일한 트랜잭션을 두 번 이상 제출할 수 있으며 결과는 여전히 같습니다.
aws dynamodb transact-write-items –client-request-token TRANSACTION1 –transact-items ‘[ { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”b201c1f2-238e-461f-88e6-0e606fbc3c51″}, “userName”:{“S”:”btables”}, “email”:{“S”:”bobby.tables@gmail.com”}, “fullName”:{“S”:”Bobby Tables”}, “phoneNumber”:{“S”:”+1-202-555-0124″} } }}, { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”userName#btables”} } }}, { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”email#bobby.tables@gmail.com”} } }}
]’
이제 항목을 나열하면 세 항목이 모두 생성 된 것을 볼 수 있습니다.
aws dynamodb scan –table-name User{ “Count”: 3, “Items”: [ { “userName”: { “S”: “btables” }, “pk”: { “S”: “b201c1f2-238e-461f-88e6-0e606fbc3c51” }, “fullName”: { “S”: “Bobby Tables” }, “phoneNumber”: { “S”: “+1-202-555-0124” }, “email”: { “S”: “bobby.tables@gmail.com” } }, { “pk”: { “S”: “email#bobby.tables@gmail.com” } }, { “pk”: { “S”: “userName#btables” } } ], “ScannedCount”: 3, “ConsumedCapacity”: null
}
다음은 가짜 Bobby Tables가 동일한 이메일 주소를 사용하여 가입하려고 할 때 발생하는 상황입니다.
aws dynamodb transact-write-items –client-request-token TRANSACTION2 –transact-items ‘[ { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”8ec436a8-97e6-4e72-aec2-b47668e96a94″}, “userName”:{“S”:”caulfield”}, “email”:{“S”:”bobby.tables@gmail.com”}, “fullName”:{“S”:”Phony Bobby Tables”}, “phoneNumber”:{“S”:”+1-202-555-0124″} } } }, { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”userName#caulfield”} } } }, { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”email#bobby.tables@gmail.com”} } } }
]’
An error occurred (TransactionCanceledException) when calling the TransactWriteItems operation: Transaction cancelled, please refer cancellation reasons for specific reasons [None, None, ConditionalCheckFailed]
보다시피 ConditionalCheckFailed, pk 값이“email#bobby.tables@gmail.com” 인 아이템이 이미 존재 하기 때문에 트랜잭션의 세 번째 요소가 오류를 수신했기 때문에이 트랜잭션이 실패합니다.
작은 Bobby 테이블이 가상 도메인을 등록하고 저장된 이메일 주소를 변경하려는 경우 두 항목 만 수정해야 합니다. 그러나 DynamoDB에서는 항목의 기본 키를 수정할 수 없으므로 고유한 이메일 항목을 삭제하고 새 이메일에 다른 항목을 삽입해야 합니다.
aws dynamodb transact-write-items –client-request-token TRANSACTION3 –transact-items ‘[ { “Update”: { “TableName” : “User”, “Key” : {“pk”:{“S”:”b201c1f2-238e-461f-88e6-0e606fbc3c51″}}, “UpdateExpression”:”SET email = :email”, “ExpressionAttributeValues”:{“:email”:{“S”:”bobby@tables.com”}} } }, { “Delete”: { “TableName” : “User”, “Key” : {“pk”:{“S”:”email#bobby.tables@gmail.com”}} } }, { “Put”: { “TableName” : “User”, “ConditionExpression”: “attribute_not_exists(pk)”, “Item” : { “pk”:{“S”:”email#bobby@tables.com”} } }}
]’
스캔한 결과 의도한 변경이 이루어 졌음을 보여줍니다.
aws dynamodb scan –table-name User{ “Count”: 3, “Items”: [ { “userName”: { “S”: “btables” }, “pk”: { “S”: “b201c1f2-238e-461f-88e6-0e606fbc3c51” }, “fullName”: { “S”: “Bobby Tables” }, “phoneNumber”: { “S”: “+1-202-555-0124” }, “email”: { “S”: “bobby@tables.com” } }, { “pk”: { “S”: “userName#btables” } }, { “pk”: { “S”: “email#bobby@tables.com” } } ], “ScannedCount”: 3, “ConsumedCapacity”: null
}
마찬가지로 Bobby가 인터넷에서 자신을 삭제하려는 경우 사용자와 관련된 세 행을 모두 제거해야 합니다.
aws dynamodb transact-write-items –client-request-token TRANSACTION4 –transact-items ‘[ { “Delete”: { “TableName” : “User”, “Key” : {“pk”:{“S”:”b201c1f2-238e-461f-88e6-0e606fbc3c51″}} } }, { “Delete”: { “TableName” : “User”, “Key” : {“pk”:{“S”:”userName#btables”}} } }, { “Delete”: { “TableName” : “User”, “Key” : {“pk”:{“S”:”email#bobby@tables.com”}} } }
]’
최종 스캔은 이제 테이블이 비어 있음을 보여줍니다.
aws dynamodb scan –table-name User{ “Count”: 0, “Items”: [], “ScannedCount”: 0, “ConsumedCapacity”: null
}
결론
이 패턴은 관계형 데이터베이스에서 마이그레이션하고 DynamoDB에서 고유 제약 조건을 유지해야 하는 경우에 유용하게 쓰일 수 있습니다. 단일 트랜잭션이 둘 이상의 테이블을 수정할 수 있으므로 두 개의 테이블을 사용하여 동일한 패턴을 구현할 수 있습니다. 사용자 테이블에“주 데이터”를 유지하고 두 번째 테이블을 사용하십시오. 이 테이블은 특정 속성에서 고유성을 보장하기 위해 순수하게 사용됩니다.
AWS는 DynamoDB에서 가능한 단일 테이블 디자인 개념을 유지하려고 노력 하지만 이 패턴에는 두 개의 테이블을 사용하셔야 보다 편리하게 활용하실 수 있습니다.
** 메가존 클라우드 TechBlog는 AWS BLOG 영문 게재 글 중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 게재하고 있습니다. 추가로 번역 및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS 페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달해드리도록 하겠습니다.