728x90
READ Sample DATA
- Scan API
- 전체 테이블 스캔
- 1MB 청크로 항목 리턴
- DynamoDB JSON 형식 사용
aws dynamodb scan --table-name ProductCatalog
- GetItem API
- 단일 항목만 읽기
- 전체 기본키를 지정해야함
- Dynamo DB의 읽기 : 최종 일관성 사용
- 💡 Read Consistency - 읽기 일관성(읽기 정합성)
- 읽기를 진행하는 데이터 : 테이블 / LSI / GSI / 스트림 데이터
- 테이블, LSI 모두 eventual consistency(최종 일관성), strongly consistent(강한 일관성) 지원
- 다양한 옵션
- -consistent-read : Specifying that you want a strongly consistent read
- -projection-expression : Specifying that you only want certain attributes returned in the request
- -return-consume-capacity : Tell us how much capacity was consumed by the request
eventual consistency
는strongly consistent
의 절반 가격
READING ITEM COLLECTIONS USING QUERY
- Query의 의미
- 일반적으로 쿼리는 컬렉션 전체 또는 일부를 읽는다는 의미를 갖는다.
- 🔥 일반적인 DB 에서의 “reading data from a database”만을 의미하는 내용이 아니다.
→ 구어체적(as the word)으로 “쿼리를 날린다~” 뭐 이런 간단한 개념이 아니라
정식적으로 “Query(쿼리)”라는 개념이 있는 것으로 이해하라는 뜻으로 보인다.
- 구체적인 조건 표현식을 포함해야한다.
- DB의 WHERE 절과 비슷한 역할을 수행한다고 보면 된다.
- 필터 표현식 사용시, 실제 소비되는 총 용량에는 유의미한 결과를 미치지 않지만 최종적으로 리턴되는 응답의 개수는 줄어들기때문에 영향을 미친다.
✏️ e.g. query filter expression
- Request
aws dynamodb query \\
--table-name Reply \\
--key-condition-expression 'Id = :Id' \\
--filter-expression 'PostedBy = :user' \\
--expression-attribute-values '{
":Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 1"},
":user" : {"S": "User B"}
}' \\
--return-consumed-capacity TOTAL
- Response
{
"Items": [ // 실제 리턴된 아이템 정보
{
"ReplyDateTime": {
"S": "2015-09-22T19:58:22.947Z"
},
"Message": {
"S": "DynamoDB Thread 1 Reply 2 text"
},
"PostedBy": {
"S": "User B"
},
"Id": {
"S": "Amazon DynamoDB#DynamoDB Thread 1"
}
}
],
"Count": 1, // 최종 반환된 아이템 개수
"ScannedCount": 2, // 전체 스캔을 수행한 전체 아이템 개수
"ConsumedCapacity": {
"TableName": "Reply",
"CapacityUnits": 0.5
}
}
Exercise
Read the documentation for –max-items and write two queries:
- return only the first reply to a thread
aws dynamodb query \
--table-name Reply \
--key-condition-expression 'Id = :Id' \
--expression-attribute-values '{
":Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 1"}
}' \
--max-items 1 \ // 최대 1개까지만 응답
--scan-index-forward \ // 인덱스 정렬 순서대로
--return-consumed-capacity TOTAL
- ScanIndexForward
- Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order.
- return only the most recent reply for a thread
aws dynamodb query \
--table-name Reply \
--key-condition-expression 'Id = :Id' \
--expression-attribute-values '{
":Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 1"}
}' \
--max-items 1 \
--no-scan-index-forward \ // 인덱스 정렬 순서대로 적용 x
--return-consumed-capacity TOTAL
Working with table Scans
- Scan API : 전체 아이템 스캔 후 리턴 (1MB)
- 단일 항목 뿐만 아니라, 전체 테이블 스캔 ⇒ 키 조건 표현식 ❌
- 최대 용량 제한은 1MB이지만,
--max-items
보다 더 많은 항목이 있는 경우 스캔 응답으로 NextToken이 포함되고 이를 후속 스캔 호출에 발행 → 중단 지점부터 다시 시작
✏️ e.g. --max-items 2
지정
- Request
aws dynamodb scan \\
--table-name Reply \\
--filter-expression 'PostedBy = :user' \\
--expression-attribute-values '{
":user" : {"S": "User A"}
}' \\
--max-items 2 \\
--return-consumed-capacity TOTAL
- Response
{
"Items": [
{
"ReplyDateTime": {
"S": "2015-09-15T19:58:22.947Z"
},
"Message": {
"S": "DynamoDB Thread 1 Reply 1 text"
},
"PostedBy": {
"S": "User A"
},
"Id": {
"S": "Amazon DynamoDB#DynamoDB Thread 1"
}
},
{
"ReplyDateTime": {
"S": "2015-09-29T19:58:22.947Z"
},
"Message": {
"S": "DynamoDB Thread 2 Reply 1 text"
},
"PostedBy": {
"S": "User A"
},
"Id": {
"S": "Amazon DynamoDB#DynamoDB Thread 2"
}
}
],
"Count": 3,
"ScannedCount": 4,
"ConsumedCapacity": {
"TableName": "Reply",
"CapacityUnits": 0.5
},
"NextToken": "eyJFeGNsdXNpdmVTdGFydEtleSI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9"
}
NextToken
값으로 미처 다 받지 못한 부분에 대해 Hash Key가 함께 온다.
- Request
aws dynamodb scan \\
--table-name Reply \\
--filter-expression 'PostedBy = :user' \\
--expression-attribute-values '{
":user" : {"S": "User A"}
}' \\
--max-items 2 \\
--starting-token eyJFeGNsdXNpdmVTdGFydEtleSI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9 \\
--return-consumed-capacity TOTAL
- 다음과 같이
--starting-token
으로 같이 토큰 값을 보내서 요청에 대한 응답을 다시 호출할 수 있다.
Exercise
Explore the data in the Forum table and write a scan command to return only the Forums that have more than 1 thread and more than 50 views.
- Request
aws dynamodb scan \\
--table-name Forum \\
--filter-expression 'Threads >= :threads AND Views >= :views' \\
--expression-attribute-values '{
":threads" : {"N": "1"},
":views" : {"N": "50"}
}' \\
--return-consumed-capacity TOTAL
- Response
An error occurred (ValidationException) when calling the Scan operation: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Views
Views
가 DynamoDB의 예약어이기 때문에, 별도의 값임을 명시해줘야한다.
aws dynamodb scan \\
--table-name Forum \\
--filter-expression 'Threads >= :threads AND **#Views** >= :views' \\
--expression-attribute-values '{
":threads" : {"N": "1"},
":views" : {"N": "50"}
}' \\
--expression-attribute-names '{"#Views" : "Views"}' \\
--return-consumed-capacity TOTAL
Views
가 예약어가 아님을 명시하기 위해, 다음과 같이 표현을 써준다.--expression-attribute-names '{"#Views" : "Views"}'
Inserting/Updating Data
- Insert
aws dynamodb put-item \\
--table-name Reply \\
--item '{
"Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 2"},
"ReplyDateTime" : {"S": "2021-04-27T17:47:30Z"},
"Message" : {"S": "DynamoDB Thread 2 Reply 3 text"},
"PostedBy" : {"S": "User C"}
}' \\
--return-consumed-capacity TOTAL
- 다음과 같이 아이템 명시
- Update
- update-item API : ConditionExpression 적용해서 요청
- ConditionExpression가 만족할 때의 요청만 수행
- oldMessage → newMessage
aws dynamodb update-item \\
--table-name Forum \\
--key '{
"Name" : {"S": "Amazon DynamoDB"}
}' \\
--update-expression "SET Messages = :newMessages" \\
--condition-expression "Messages = :oldMessages" \\
--expression-attribute-values '{
":oldMessages" : {"N": "4"},
":newMessages" : {"N": "5"}
}' \\
--return-consumed-capacity TOTAL
- 동일 명령 재 실행시, 오류 발생
An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed
Exercise
aws dynamodb update-item \\
--table-name ProductCatalog \\
--key '{
"Id" : {"N": "201"}
}' \\
--update-expression "SET #Color = list_append(#Color, :values)" \\
--expression-attribute-names '{"#Color": "Color"}' \\
--expression-attribute-values '{
":values" : {"L": [{"S" : "Blue"}, {"S" : "Yellow"}]}
}' \\
--return-consumed-capacity TOTAL
aws dynamodb update-item \\
--table-name ProductCatalog \\
--key '{
"Id" : {"N": "201"}
}' \\
--update-expression "SET Messages = :newMessages" \\
--condition-expression "Messages = :oldMessages" \\
--expression-attribute-values '{
":oldMessages" : {"N": "4"},
":newMessages" : {"N": "5"}
}' \\
--return-consumed-capacity TOTAL
Deleting Data
- delete-item
aws dynamodb delete-item \\
--table-name Reply \\
--key '{
"Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 2"},
"ReplyDateTime" : {"S": "2021-04-27T17:47:30Z"}
}'
- delete-item 수행 시, 동일한 쿼리를 두번 날리면 아무런 일 발생 ❌
Transactions
- DynamoDB : 최대 100개의 작업 요청 → 동기식 쓰기 작업
- 트랜잭션 4mb 크기 제한
- 작업 모두 성공 or 모두 실패하도록 원자적 완료
- 멱등성 : 동일한 트랜잭션 두 번 이상 보낼 수 있음, But 해당 트랜잭션은 한번만 실행
- 자체적으로 멱등성이 없는 API 사용시 유용
Global Secondary Indexes
- 타 DB 서비스의 인덱스 개념
- GSI : 다양한 파티션 & 정렬 키 중심으로 데이터 자동 로테이션
- Query & Scan API 를 통해 더 많은 액세스 패턴 제공을 위한 데이터 그룹화 및 정렬
- GSI 생성
aws dynamodb update-table \
--table-name Reply \
--attribute-definitions AttributeName=PostedBy,AttributeType=S AttributeName=ReplyDateTime,AttributeType=S \
--global-secondary-index-updates '[{
"Create":{
"IndexName": "PostedBy-ReplyDateTime-gsi",
"KeySchema": [
{
"AttributeName" : "PostedBy",
"KeyType": "HASH"
},
{
"AttributeName" : "ReplyDateTime",
"KeyType" : "RANGE"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 5, "WriteCapacityUnits": 5
},
"Projection": {
"ProjectionType": "ALL"
}
}
}
]'
- scan 명령 대신에, query 명령을 통해 정렬된 사용자가 작성한 모든 Reply 탐색
aws dynamodb query \\
--table-name Reply \\
**--key-condition-expression 'PostedBy = :pb' \\**
--expression-attribute-values '{
":pb" : {"S": "User A"}
}' \\
--index-name PostedBy-ReplyDateTime-gsi \\
--return-consumed-capacity TOTAL
- 기본 테이블 실행과 동일한데, GSI 키 속성을 사용해야함
- 인덱스에서
get-item
명령어가 안되는 이유- get-item 요청은 최대 항목과 일치하는 지 의미하는 행을 추가
- GSI의 키가 단일 항목에 대해 고유하게 식별한다는 보장 존재 ❌
🙋🏻♀️ Questions
- eventual consistency 🆚 strongly consistent 가 가격 차이가 나는 이유?
결과적 일관성 / 최종적 일관성
- eventual consistency?
- 분산 컴퓨팅 환경과 크게 밀접한 관련이 있다.
- 단기적으로 일관성을 잃더라도 결국에는 일관성을 유지하는 모델
Eventual consistency는 항목이 새롭게 업데이트되지 않는다는 전제하에 항목의 모든 읽기 작업이 최종적으로는 마지막으로 업데이트된 값을 반환한다는 것을 이론적으로 보장한다.
e.g. DNS
- strongly consistency?
- 전통적인 RDB에서의 데이터 정합성 문제가 이에 해당한다.
- 조회된 데이터가 해당 항목을 보는 모든 사용자에게 일관적으로 표시된다.
- But, 애플리케이션의 확장성과 성능을 어느 정도는 포기해야할 수도 있다.
- 데이터의 정합성을 엄격하게 유지해야하기 때문에, 데이터 접근에 따른 Lock 설정을 통해 데이터를 긴밀하게 관리해야한다.
적합한 예시의 한 형태
strong consistency를 필요로 하지 않는 사용 사례
e.g 1) '친구 목록에서 특정 시간에 온라인 상태인 사용자 확인하기'
e.g 2) '게시물에서 몇 명의 사용자가 +1을 눌렀는지 확인하기'strong consistency를 필요로 하는 경우
e.g 3) '사용자가 결제 프로세스를 완료했는지'
e.g 4) '게임 플레이어가 한 전투 세션 동안 획득한 포인트'
- DynamoDB 의 인덱싱 알고리즘?
- DynamoDB 내부 동작
- 추가는 맨 끝에 하면 되지만 스캐닝은 어려움
- key-value 쌍을 리스트로 파일에서 관리한다.
- Hashmap을 이용한 저장방법
- 인덱스 활용 → 포인터 오프셋을 이용한 데이터 탐색
- 원본 테이블 보다 더 적은 사이즈로 데이터 읽기가 가능
🔗 Reference
- Datastore로 strong consistency와 eventual consistency 간에 균형 유지
- https://medium.com/swlh/building-dynamodb-brick-by-brick-237e0008b698
- 책 마이크로 서비스 아키텍처 구축
- 책 데이터 중심 애플리케이션 설계
728x90
'⚙️ DevOps > AWS' 카테고리의 다른 글
[AWS/DynamoDB] Backup / Modeling & Migration (51) | 2024.02.01 |
---|---|
[AWS/EFS, API Gateway, Lambda] 비디오 서버 서버리스로 마이그레이션 하기 (45) | 2024.01.28 |
[AWS/DynamoDB] Overview (2) | 2024.01.17 |
[AWS/Secrets Manager] 키 관리 Secrets Manager 로 간단히 하기 (32) | 2023.12.09 |
[AWS/Elastic IP] EC2에 탄력적 IP 주소 할당하기 (0) | 2022.08.09 |