AWS DynamoDB(NoSQL PaaS, Key/Value Store)
NoSQL의 사용 이유(RDBMS 한계?)
Row->Document 성 데이터 이동
Scale-up의 한계 -> Scale-out 가능
비정형 데이터의 최적화
DynamoDB는 Partition과 Sort Key(옵션)만을 지원한다.
Sort Key를 생성하고 DynamoDB SDK의 get_item() API를 사용할 경우 Partition + Sort Key 조합만 지원한다.
만약 Sort Key를 생성했음에도 Partition Key만을 사용할 경우에는 필터를 걸거나, query() API를 사용해야 한다.
자유롭게 CRUD API를 구성하려면 GET, PUT Item 등의 API 보단 Query API 사용을 권장한다.
(필터의 경우 전체 데이터를 받은 후에 사용되기 때문)
기존 테이블의 Partition , Sort Key만으로는 다양한 쿼리의 제약이 있을 수 있기 때문에 DynamoDB는 추가로 두 가지의 인덱스를 지원한다.
이것을 Local Secondary Index(LSI), Global Secondary Index(GSI) 라고 부른다.
LSI는 테이블 생성 시에만 만들 수 있으며, Partition Key는 기존 테이블의 키와 같게 지정해야 한다.
또한 인덱스 크기의 경우 10GB 제약이 존재한다.
같은 Partition Key에 대해 Sort Key가 추가로 필요할 경우 테이블 생성 시에 추가로 LSI를 지정할 수 있다.
GSI는 기존 테이블의 Partition Key와 별개의 Partition Key를 만들 수 있기 때문에 물리적으로 새로운 파티션에 저장된다.
따라서 인덱스 크기 제한은 없으며, Read/Write Capacity를 별개로 지정할 수 있다. 그러나 Async 방식으로 동작하기 때문에 데이터의 무결성에 문제가 발생될 수 있다.
두 인덱스 모두 최대 5개까지 지정 가능하다.
Item 검색을 위해서는 Partition + Sort Key만 사용할 수 있다. 이 외 Attribute는 조건문의 값으로 사용할 수 없다. 딱 필요한 Key만을 사용하여 Item들을 검색할 수 있다.
가수(Partition), 노래(sort), 앨범, 발매 시기와 같은 Item이 있다면 가수&노래 혹은 가수만으로만 해당 Item을 검색할 수 있다.
만약 앨범을 통한 검색 기능도 필요하다면 GSI를 이용하여 앨범을 Partition Key로 등록하고 쿼리 결과를 원하는 Attribute를 추가할 수 있다.
그러나 GSI 비용이 증가되기 때문에 쿼리량이 많지 않다면, 기본 Partition Key로 스캔하고 필터를 이용하는 게 나을 수도 있다.
또한 발매 시기 별로 가수의 노래를 검색하고 싶다면, 테이블 생성 시에 발매 시기를 Sort Key로 하는 LSI를 만들 수도 있다.
아무튼 기존 RDBMS 모델링 방식과 다르게, API Response 기준으로 DynamoDB를 모델링 해야한다. 제약이 많다. 복잡한 모델링이 필요한 DB에는 어울리지 않는다.
기존 DBA도 DynamoDB를 위한 데이터 모델링을 따로 연구(?)해야 할 것이다. 어렵다... 나만 어렵나?
그런데 왜 DynamoDB를 쓸까?
그러나 이런 단점에도 불구하고, DynamoDB의 경우 거의 모든 것을 관리해주기 때문에 매우 매력적인 서비스다. 스토리지 자동 확장, IOPS 관리 등 (Aurora도 스토리지 자동 확장은 지원하나, IOPS 병목 문제는 따로 관리해야 한다.)
아래 포스팅은 Cassandra -> DynamoDB 이관을 경험한 엔지니어(?)의 경험이 담겨있는 DynamoDB 관련 글이 시리즈로 연재되어 있다. 강추...
API Gateway + Lambda + Dynamodb 조합이 꿀
Stateless 한 DynamoDB는 Lambda와 궁합이 잘 맞는다. 실제 패킷을 보면 443 Port(AWS API)로 동작하며, Say Hello 하고 Bye 한다.
반면 MongoDB의 경우 mongo mongo 하고 Connection을 물고 있는 것을 알 수 있음. 일반적으로 사용하는 mongoose connection의 경우.
참고: https://www.youtube.com/watch?v=RfOt2bo0bSA(40분 영상인데 더 보강된 발표가 계속 나오므로 최근의 영상부터 시청 권장)
DynamoDB의 API scan 등을 사용하면 "L", "M", "S" 등 각 Value의 Type까지 가져온다. json을 파싱할 때 거슬린다. 아래와 같이 하위 API를 wrapping 한 모듈을 사용하면 편리하다.
node.js의 경우 const dynamodb = new aws.DynamoDB.DocumentClient();
Python의 경우 boto3.resource('dynamodb')
boto3.client vs boto3.resource
지원하는 data type은 아래와 같다. date와 같은 type은 없으나, String을 통해 date type과 같이 사용 가능하다. 또한 float type도 없다. Decimal을 통해 json 데이터 변환 후 저장을 해야 한다.
DynamoDB 패키지는 없을까?
여기까지는 일단 패키지 없이 사용한 내용이고, DynamoDB도 dynamoose라는 ORM 관련 패키지가 있다. 직관적인 예제 등으로 설명이 잘 되어 있다.
사용법은 기존 mongoose와 비슷하나, connect가 따로 없기 때문에 mongoose connect 부분에서 Dynamoose의 createDynamooseInstance(); 를 이용해야 한다. 또한 population과 같은 다양한 기능도 지원 한다.
DynamoDB 쿼리 제약은?
추가로 GET Item은 1MB의 제약이 있어, scan과 같은 명령어 사용 시에는 LastEvaluatedKey 값을 체크한 후 재귀방식으로 실행이 필요하다.
DynamoDB는 IOPS 컨트롤은?
DynamoDB의 Storage는 Aurora와 같이 자동 확장 해준다. 또한 아래 그림과 같이 기준 IO를 설정하고 임계치에 대해 AutoScaling 기능을 사용할 수 있다.
추가로 On-demand라는 기능도 제공한다. 사용자는 IO에 대해 전혀 신경쓸 필요가 없어 진다. 물론 몇몇 인덱스에 IO가 몰리면 hot spot에 따른 스키마 변경이 필요할 수도 있긴 하다. 인덱스 키를 해쉬로 골고루 분산되게 적용했으면 회피할 수 있다.
실제 사용하면서 계속 내용을 덧붙여서 두서가 없다. 추가할 내용이 있으면 계속 업데이트 하겠음.
DynamoDB는 저렴한 서비스일까?
이건 데이터 모델링에 따라 API 설계에 따라 매우매우 달라질 수 있다.
우선 절대적인 DynamoDB의 가격은 저렴하지 않다.
먼저 스토리지를 예로 들면 $0.27/1GB 다.
(S3의 경우 $0.025/1GB, RDS Aurora의 경우 $0.12/1GB)
따라서 스토리지 비용은 S3 대비 약 10배, RDS 기준으로는 약 2배 비싸다.
또한 여기에 Item 별로 100Byte가 추가된다. 가령 Item이 10,000,000개라면 1GB 스토리지 비용이 추가된다. 용량이 작은 Item들이 늘어나다 보면 절대 무시하지 못할 금액이다.
따라서 스토리지의 경우 TTL을 이용한 데이터 삭제 작업은 필수다. 사용하지 않는 데이터는 S3로 옮기자.
그리고 Read/Write API 비용이 추가된다.
Read의 경우 4KB 기준으로 1Unit이 필요하고, Write의 경우 1KB 기준으로 1Unit이 필요하다. Item들의 미세한 크기 차이에 따라 비용이 2배가 될 수도 있다.
또한 일관된 요청이나 트랜잭션 처리를 할 경우 2배의 Unit이 필요하다.
데이터 모델링에 따라 가격이 정말 최소 2배.. 최대는 정말 어마어마 하게 비용 차이가 날 것으로 생각된다.
따라서 DynamoDB를 주력으로 사용한다면 최우선 고려 사항은 비용에 따른 데이터 설계라고 생각한다.
내 생각에 DynamoDB의 뉴비와 고수는 "얼마나 비용 절감을 할 수 있는지" 같다. 확장성이야 단순하게 생각하면 칼럼만 추가하거나 인덱스(비용 증가)를 추가하면 되기에...
IOPS Provisioning vs On-demand 중에 뭘 써야 할까?
Provisioning vs On-demand 비용을 비교하면, Provisioning의 경우 Write/Read Capacity를 기준으로 비용 발생
On-demand의 경우 DynamoDB 요청 수를 기준으로 비용 발생
일반적으로 최적화 된 Provisioning이라면 가격이 더 저렴할 것으로 보이지만, 최초 On-demand를 사용하고 요청 수를 계산하여 Provisioning과 On-demand 비용 비교가 필요하다.
또한 아래와 같이 DynamoDB는 Global Table이라는 기능을 지원한다. 무엇인고 하면, 원하는 리전을 추가하면 각 리전의 DynamoDB의 Table끼리 데이터 동기화가 된다. 개꿀.
Write/Read 에 대한 모든 데이터가 동기화 된다. 그러나 Table Item들이 이미 있으면 생성이 불가능하므로, 최초에 생성해야 한다.
DynamoDB로 글로벌 서비스할 때는?
글로벌 서비스 시에 매우매우매우 유용하다. 각 리전별 App 서버에서 Endpoint만 변경해주면 된다.
경험치를 키우기 위해 아래 토이 프로젝트의 DB를 DynamoDB로 사용해봤다.
(글로벌 노드를 두고 URL에 대한 Latency와 Header 값을 체크해주는 서비스)
스키마 변경과 성능 이슈에서 자유로워진다는 것이 가장 큰 장점이다. 또한 글로벌 배포 또한 쉽게 가능하다.
단점은 Hash, Range Key 만을 사용하기 때문에 복잡한 모델링의 데이터는 사용하기 힘들다.
웹페이지 게시판 같은 용도에 사용할 때 페이징 처리는 RDB와 같이 쉽게 할 수 없다.
로그 등 단순하고 대규모 Row 데이터 적재가 고민이라면 고려할만하다. (물론 TTL로 데이터 정리는 필수)
응, 아니야 몽고디비 쓸거야
아래는 DynamoDB용 Client Tool