AWS

Amazon DynamoDB 개념 정리

Chaein.P 2022. 5. 15. 17:38

이 포스트는 Amazon DynamoDB Workshop & Labs을 참고하여 정리한 study log 입니다.

DynamoDB란?

Amazon DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability.

  • fully managed NoSQL database
    : DynamoDB는 완전 관리형 NoSQL 데이터베이스이다. 완전 관리형이란 리소스 제공자인 클라우드가 하드웨어 프로비저닝, 설정 및 구성, 복제, 소프트웨어 패치 적용 또는 클러스터 확장 등과 같은 데이터베이스 운영 및 확장에 필요한 관리를 자체적으로 수행해 개발자의 운영부담을 줄이고 보다 비즈니스 로직 설계에 집중할 수 있도록 지원하는 서비스를 말한다.
  • fast and predictable performance
    : DynamoDB는 빠르고 예측 가능한 성능을 보여준다. DynamoDB를 사용하면 사용자가 원하는 만큼 데이터를 저장하고 검색할 수 있고 모든 수준의 요청 트래픽을 처리할 수 있다.
  • seamless scalability
    : DynamoDB는 다운타임이나 성능 저하 없이 테이블의 처리 용량을 확장하거나 축소할 수 있다.
  • High Availability and Durability
    : DynamoDB는 데이터의 일관성과 빠른 성능을 유지하면서 처리량에 따라 자동으로 다수의 서버에 트래픽을 분산시켜 처리한다. 또한 모든 데이터는 SSD에 저장되고 자동으로 여러 AZ에 복제돼 높은 가용성을 제공한다.

DynamoDB의 부가 기능

  • 보안
    : DynamoDB는 자체적인 데이터 암호화 기능을 갖고 있어 민감한 데이터를 보호하는 데 수반되는 운영 부담과 복잡성을 제거한다. 암호화 키 종류는 3가지가 있고 default는 AWS owned keys이다. default 설정을 사용하면 추가 과금은 없다.
  • 온디맨드 백업
    : DynamoDB는 온디맨드 백업 기능과 특정 시점으로 데이터를 원복시킬 수 있는 기능을 제공한다.
  • 아이템 자동 삭제
    : TTL(Time to Live)을 사용해 만기된 데이터를 자동으로 삭제해주는 기능을 제공한다.

DynamoDB 구성요소

  • Table : 아이템의 집합 (RDBMS의 테이블과 같은 개념)
  • Item : 어트리뷰트의 집합 (RDBMS의 row와 같은 개념)
  • Attribute : 쪼개질 수 없은 가장 작은 데이터 단위

Primary key

primary key는 하나의 아이템을 식별할 수 있는 고유한 값, 또는 고유한 쌍이다. 고로 두개의 아이템이 같은 primary key를 가질 수 없다. DynamoDB는 두가지 종류의 primary key 제공한다.

  • partition key (단일키)
    : DynamoDB는 파티션 키의 값을 내부 해시 함수에 대한 input 으로 사용한다. 해시 함수의 출력은 항목이 저장될 파티션(DynamoDB 내부의 물리적 스토리지)을 결정한다. 만약 primary key가 partition key 로만 구성된다면 각 아이템의 partition key는 고유해야한다.
  • partition key + sort key (복합키)
    : 여러개의 아이템이 동일한 partition key를 갖고 있다면 sort key로 primary key를 구성한다. partition key 와 sort key 쌍은 고유한 쌍이어야 한다. 같은 partition key를 갖는 아이템은 모두 동일한 partition에 저장되는데 이때 sort key로 정렬되어 저장된다. sort key를 사용하면 partition key로 할 수 없는 범위 설정이 가능하기 때문에 보다 유연한 조회가 가능해진다.

partition key 는 RDBMS의 해시 인덱스 개념과 비슷하고 sort key는 Btree 인덱스 개념과 비슷하다.

Secondary Indexes

DynamoDB는 보조 인덱스를 추가로 생성할 수 있다. DynamoDB에서는 Primary Key 만 쿼리 조건으로 사용할 수 있기 때문에 보조 인덱스를 추가하면 더 유연한 쿼리 사용이 가능해진다. 그러나 Secondary Index를 생성하면 해당 Secondary Index를 기준으로 하는 가상의 테이블이 추가로 생성되기 때문에 스토리지 비용을 증가시킨다. Secondary Index에는 두 종류가 있다.

  • GSI (Global Secondary Index)
    • primary key를 제외하고 테이블에 존재하는 모든 attribute로 GSI를 생성할 수 있다.
    • GSI 는 partition key만 존재하는 단일키로 생성할 수 있고 partition key와 sort key 조합인 복합키로도 생성할 수 있다.
    • GSI는 테이블 생성 이후에도 추가로 생성, 수정, 삭제가 가능하다.
    • 인덱스 용량에 제한이 없고 base table 설정과 별도로 읽기/쓰기 처리량을 프로비저닝할 수 있다.
  • LSI (Local Secondary Index)
    • partition key는 기존 테이블과 동일한 상태에서 sort key를 추가로 생성하는 것을 의미한다.
    • LSI는 테이블 생성시 함께 생성하며 이후 추가 생성, 수정, 삭제가 불가능하다.
    • Item Collection은 10GB를 초과할 수 없고 프로비전된 읽기/쓰기 처리량은 base table 설정을 따른다.
    💡 Item Collection이란? 하나의 테이블에 같은 partition key를 가지고 있는 모든 아이템과 같은 partition key를 가지고 있는 모든 LSI 아이템의 집합을 의미한다.
    Item Coollection

    위의 그림에서 Thred라는 테이블의 S3 라는 같은 partition key를 가지고 있는 아이템과 LastPostIndex 테이블의 S3라는 partition key를 가지고 있는 아이템은 같은 Item Collection에 속한다.

하나의 테이블에는 최대 20개의 GSI, 5개의 LSI를 설정할 수 있다. 일반적으로 보조 인덱스가 필요할 시 GSI 사용을 권장하며 아주 strongly consistency 이 요구되는 경우에만 사용한다. GSI는 eventually consistency 만 지원하기 때문이다.

💡 strongly consistency 와 eventually consistency

  • eventually consistency : 데이터 조회시 응답에 가장 최신의 결과가 반영되지 않을 수 있다.
  • strongly consistency : 가장 최근에 발생한 쓰기 결과가 성공적으로 반영된 이후의 결과를 응답한다. 그러나 strongly consistency는 다음과 같은 단점을 갖는다.
    • 네트워크 지연 또는 일시적 중단이 발생하면 Dynamodb는 500 에러를 응답할 가능성이 높아진다.
    • eventually consistency보다 더 긴 대기시간이 발생할 수 있다.
    • GSI는 지원하지 않는다.
    • eventually consistency 보다 더 많은 read/write 처리량을 사용한다.
      기본적으로 DynamoDB의 검색 설정은 eventually consistency이다. Strongly consistency가 필요하다면 조회시 ConsistentRead 옵션을 true로 설정해주어야 한다.
    • eventually consistency에 비해 약 두배 이상의 read 용량을 소비해 비용도 두배 가까이 비싸다.

✅ GSI는 partition key 또는 partition key와 sort key의 조합이 unique 하지 않아도 된다.

Secondary Indexes 사용 유의사항

  • 인덱스는 최소한으로 사용한다. 자주 사용하지 않는 인덱스는 스토리지와 I/O 비용만 증가시키기 때문이다.
  • 프로젝션할 attribute 선택은 신중히 한다. secondary index는 추가 스토리지와 provisioned 처리량을 사용하기 때문에 인덱스 용량은 가능한 최소로 유지해야한다. 인덱스 용량이 적을수록 성능은 향상된다.
  • 쿼리는 fetch를 피하는 방향으로 최적화한다. 만약 project 되지 않은 attribute를 LSI로 쿼리하면 DynamoDB는 해당 attribute를 찾기 위해 결국 테이블의 전체 아이템을 다시 조회하게 된다. 이는 성능에 좋지 않을 뿐더러 추가적안 I/O 비용을 발생시킨다. 따라서 index 생성시에는 해당 인덱스로 조회할 attribute를 고려해 project 해야한다.
  • LSI를 사용할 때에는 용량을 넘기지 않게 유의한다. LSI를 사용할 때에는 특정 partition key를 갖는 item의 총합이 10GB 를 넘기지 않는지를 충분히 고려해야한다. 만약 item collection이 10GB를 넘게 되면 해당하는 parition key를 갖는 데이터를 더이상 추가할 수 없게 된다.

Read/Write Capacity Mode

Read/Write Capacity Mode는 Read/Write 처리량에 대한 과금 방식과 Read/Write 용량을 어떻게 관리할 것인지를 결정한다. Read/Write Capacity Mode에는 두가지가 있다.

  • on-demand
    용량 계획 없이 사용자가 실제 사용한 읽기/쓰기 용량 만큼 과금하는 유연한 청구 옵션이다. 트래픽을 예측하기 어려울 때 선택하면 좋은 옵션이다. on-demand 옵션에서는 워크로드가 이전에 도달했던 트래픽 범위 안에서 변동이 있으면 DynamoDB가 즉각적으로 수용한다. 만약 이전에 도달했던 트래픽을 초과하게되면면 이전 최대 트래픽의 두배까지는 즉각 수용이 가능하다. 그러나 최대 트래픽을 넘긴지 30분 이내로 그 두배 이상의 트래픽이 발생하면 throttle이 발생할 수 있다. on-demand 옵션에서는 read/write 처리량을 따로 지정해줄 필요가 없다. 사용자가 사용한 만큼 RRU와 WRU 에 기반해 요금을 청구한다.
    • RRU(ReadRequestUnits)
      하나의 RRU는 최대 4KB 아이템 하나에 대한 Strongly Consistency 요청 하나 또는 Eventually Consistency 요청 두개를 의미한다. 두개의 RRU는 최대 4KB 아이템에 대한 transactional read 하나를 담는다. 예를 들어 8KB 아이템/아이템들을 조회할 때 Strongly Consistency는 2개의 RRU를 갖고 Eventually Consistency는 한개의 RRU를 갖는다. transactional read 라면 4개의 RRU가 요구된다.
    • WRU(WriteRequestUnits)
      하나의 WRU는 최대 1KB 아이템 하나를 쓰는 요청 하나를 말한다. transactional write는 한번 쓰기 요청에 2WRU가 필요하다.
  • provisioned
    애플리케이션 사용에 필요한 초당 읽기 및 쓰기 횟수를 지정한다. 트래픽이 예측 가능하거나 또는 일관되거나 비용에 제한을 두고 싶을 떄 사용할 수 있는 방법이다. autoscaling 설정을 통해 가변적인 트래픽에 따라 읽기/쓰기 횟수에 변화를 줄 수 있다.
    • RCU(ReadCapacityUnits)
      하나의 RCU는 최대 4KB 아이템에 대해 초당 한번의 Strongly Consistency 요청 또는 두번의 Eventually Consistency 요청을 의미한다. 두개의 RCU는 최대 4KB 아이템에 대한 초당 한번의 transactional read를 담는다.
    • WCU(WriteCapacityUnits)
      하나의 WCU는 최대 1KB의 항목에 대해 초당 한 번의 쓰기를 나타낸다. 두개의 WCU는 최대 1KB 항목에 대해 초당 한번이 transactional write를 의미한다.

RRU가 6이면 24KB가 read 됐다는 뜻, RCU가 6이면 초당 최대 처리량이 24KB 라는 뜻

Read CLI

scan

scan은 전체 테이블 검색을 수행하고 1MB 청크 이내로 조회한 아이템들을 반환한다. DynamoDB를 사용한 read 중에 가장 느리고 비싼 방법이다.

aws dynamodb scan --table-name ProductCatalog

scan은 primary key를 이용한 조회가 아니기 때문에 테이블 전체를 scan 하지만 Filter Expression 을 조건으로 걸어준다면 반환되는 데이터 양은 줄여 네트워크 성능을 개선시킬 수 있다.

aws dynamodb scan \
    --table-name Reply \
    --filter-expression 'PostedBy = :user' \
    --expression-attribute-values '{
        ":user" : {"S": "User A"}
    }' \
    --return-consumed-capacity TOTAL

만약 조회결과가 1MB가 넘거나 --max-items 만큼 조회하고 남은 아이템이 있다면 조회 반환값에 포함되지 않은 다음 아이템은 NextToken으로 전달 된다. 다음 scan api를 사용할 때 starting-token에 NextToken 값을 넣어주면 이전에 조회되었던 item의 다음 item부터 반환된다.

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

get-item

primary key를 사용해 특정 아이템을 조회할 때 사용한다.

  • consistent-read : strongly consistency 사용 옵션
  • projection-expression : 응답에 포함한 attribute 특정
  • return-consumed-capacity : 해당 요청이 사용한 조회 용량 함께 반환
  • aws dynamodb get-item \ --table-name ProductCatalog \ --key '{"Id":{"N":"101"}}' \ --consistent-read \ --projection-expression "ProductCategory, Price, Title" \ --return-consumed-capacity TOTAL

query

Item Collection 에 속한 Item을 read할 때 사용한다. Item Collection은 같은 partition key, 다른 sort key를 같은 아이템의 집합이다. query 를 하기 위해선 key-condition-expression을 정의해주어야 하는데 sql의 where절로 partition key와 sort key 조건을 걸어주는 것과 비슷하다고 볼 수 있다.

primary key가 아닌 non-key-attribute인 경우에는 Filter Expression을 사용한다.

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

Insert/Update/Delete CLI

put-item

새로운 Item을 추가할 때 사용한다.

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-item

기존 item의 데이터를 수정할 때 사용한다. 특정 조건을 만족했을 경우에만 update 하고 싶다면 --condition-expression 을 사용해 조건을 추가한다. 조건에 만족되지 않으면 update 되지 않는다.

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

delete-item

아이템을 삭제할 때 사용한다. 여러 아이템을 동시에 삭제할 수 없다. 아이템을 삭제하기 위해선 full primary key를 알아야한다.

aws dynamodb delete-item \
    --table-name Reply \
    --key '{
        "Id" : {"S": "Amazon DynamoDB#DynamoDB Thread 2"},
        "ReplyDateTime" : {"S": "2021-04-27T17:47:30Z"}
    }

delete-item은 존재하지 않은 item을 삭제하거나 같은 명령을 여러번 실행하거나 하더라도 에러를 반환하지 않으므로 주의한다.

transactions

DynamoDB의 TransactWriteItems API를 사용하면 최대 25개의 요청(총 4MB 이하의 transaction)을 동기적으로 동작시킬 수 있다. 이 때 이 요청 그룹은 원자성을 띄어 하나라도 실패하면 transaction 전체가 실패한다. DynamoDB의 transaction은 멱등성의 법칙을 지켜 같은 transaction이 여러번 실행되더라도 한번만 실행된다.

--client-request-token은 Idempotency Token(멱등성 토큰)이라고도 하는데 이 토큰이 같은 경우 멱등성의 법칙이 부여된다.

aws dynamodb transact-write-items --client-request-token TRANSACTION1 --transact-items '[
    {
        "Put": {
            "TableName" : "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"}
            }
        }
    },
    {
        "Update": {
            "TableName" : "Forum",
            "Key" : {"Name" : {"S": "Amazon DynamoDB"}},
            "UpdateExpression": "ADD Messages :inc",
            "ExpressionAttributeValues" : { ":inc": {"N" : "1"} }
        }
    }
]'

백업

DynamoDB는 두 종류의 백업을 지원한다.

  • on-demand
    온디맨드 백업은 사용자가 백업이 필요할 때마다 직접 백업하는 방식이다. 백업된 데이터는 사용자가 직접 삭제하기 전까지 삭제되지 않는다.
  • point-in-time-recovery(pitr)
    PIRT은 rolling window 방식으로 보관 기간은 35일로 고정이며 수정할 수 없다. PITR이 활성화되어있다면 35일 이내 특정 시점으로 되돌릴 수 있다.

보다 정교화된 백업, 혹은 스케쥴링 백업 작업이 필요하다면 AWS Backup 을 추가로 이용해야한다.

참고