DynamoDB 사용하기

Heehong Moon
bgpworks
Published in
7 min readAug 10, 2018

항상 서비스를 만들다 보면 직면하는 문제가 DB Scaling에 관련된 부분이다. 웹서버야 앞에 로드밸러서 붙이고 늘려나가면 되는데, DB쪽에서는 사실 사용자가 많아지면 답이 없다. DB부하가 커지면 쿼리 속도가 느린 부분 고쳐주고, Memcached나 Redis같은 메모리 DB로 캐슁을 빡세게 한다. 그래도 안되면 DB 사양을 늘려야 하고, DB사양 늘리는 것도 한계가 있기 때문에 특정 부분의 데이터를 빼서 다른 최적화된 DB로 일부 데이터를 옮겨 처리 해야 한다.

DynamoDB를 처음 보기 이전에 2011년쯤 나는 Google AppEngine의 DataStore(https://cloud.google.com/datastore/docs/concepts/overview)를 사용해서 게임 서버를 만든 적이 있다. DataStore는 다른 RDB의 방식과 다르게 NoSQL이면서, 문서에 적힌대로 테이블을 디자인하고 사용하면 거의 무제한에 가깝게 Scaling이 된다는 점에 매료되었다. 물론 무제한 Scaling을 보장하려고 꽤많은 문서와 동영상을 보면서 공부했던 기억이 난다. 다른 RDB는 아무리 내가 테이블 디자인을 잘 한다고 한들, 사용자가 많아지면 언제 DB가 뻗을지 모른다는 걱정을 해야 하지만, 잘만 디자인해두고 사용하면 언제든 발뻗고 잘수 있다는게 어딘가.

AWS의 서비스를 꽤 많이 써왔고, 새로 나오는 제품이 바로 적용가능한지 테스트 해보는데 DynamoDB는 Google DataStore에 대한 기억 때문인지 항상 써보고 싶지만 포기하게 되는 서비스 였다. 매번 써보기를 원했지만 실패했던 이유는 여러가지가 있는데, 몇년이 지나 다시 보게 되었는데 이제는 써봐도 좋겠다는 생각이 들었다.

자동 Scaling

DynamoDB는 다른 DB와 다르게 Read capacity, Write capacity를 기준으로 비용이 책정된다. 여기서 말하는 capacity는 초당 얼마나 많은 read/write를 할 수 있는가에 대한 것이고, 다른 말로 하면 초당 얼마나 많은 데이터를 read/write를 허용할지 미리 정해둔 값만큼 지불하게 된다.

처음 접했을때는 내가 얼마나 read/write를 할지 모르는데 어떻게 미리 정해두는지 궁금했는데, 일반적으로 보통 DB 인스턴스를 만들때처럼 적당히 큰 값을 미리 정해두고 사용하는 방식이었다. 내가 허용한 capacity를 넘어서면 알림을 받아서 더 키우기도 하고, 마케팅 캠페인이 시작되기 전 capacity를 미리 올려두고 하는 것이다.

미리 capacity를 정하는게 왠지 골아픈것 같았는데, 최근에는 auto scaling 기능이 들어가서 내가 정한 capacity에 다다르면 자동으로 키우거나 줄이는 동작을 자동으로 해준다. 보통 서비스의 트래픽이 매일 일정한 주기를 그리게 되는데, auto scaling을 활성화 하면 비용이 절감된다.

무제한 Scaling

DynamoDB의 capacity 최대값은 40,000 read/write capacity 인데, 이 정도면 1KB짜리 데이터 4만개를 매초 읽을 수 있는 정도 이다. capacity는 1KB 데이터와 1:1매칭이 되는게 아니라 정확히 말하면 더 복잡하지만 대략적으로 그렇다는 것이다. 그리고 이것보다 더 많은 capacity가 필요하면 AWS support에 연락하면 더 오픈해준다.

DynamoDB가 아름다운 점은 내가 정해둔 capacity내에서의 PutItem, GetItem은 항상 10ms 이내로 리턴되고, Query는 20~30ms내에 리턴된다.

무제한 Scaling을 위한 테이블 디자인

아무리 내가 높은 capacity를 지정해두더라도, 내가 제대로 디자인 하지 않고 막 쓰게 된다면 DynamoDB는 Scaling이 되지 않는다. 가장 기본적인 내용은

전체 데이터를 골고루 분산 할 수 있는 Partition Key를 사용해야 한다.

DynamoDB는 DB트래픽이 늘어나면 자동으로 Instance를 늘리게 되는데, 그 기준이 되는것이 Partition Key를 Hash한 값으로 분산된다. 그리고 한 Partition Key에 해당하는 데이터를 10GB를 넘을 수 없다.

로드가 적은 DynamoDB 테이블을 사용하고 있을때는 한 서버로 처리를 모두 하다가, 로드가 많아지면 DynamoDB는 이미 있는 Partition Key의 Hash값으로 두개의 서버로 쪼개지게 된다. 그래도 부하가 크다면 또 쪼개지는 과정을 반복하게 된다. 이러한 원리로 DynamoDB는 부하가 많아져도 여러개의 작은 DB로 로드가 분산된다. Scaling의 원리를 보면, Partition Key를 어떻게 고르는가가 가장 핵심적인 키라는걸 알수 있다.

어려운 Partition Key 정하기

Key-value Storage의 용도로 DynamoDB를 사용하면 사실 아무 고민없이 사용하면 된다. 대신 Key를 적당히 랜덤이 섞인걸 사용하면 된다. Put/Get이 10ms이내로 나오고 무제한으로 Scaling이 되니 이보다 좋은 DB가 어디 있겠나 싶다.

Partition Key를 정하기 어려운 점은 DynamoDB에서 Query기능을 사용하려고 할때이다. 내가 아무리 간단한 서비스를 만든다고 하더라도 리스트의 순서를 최신순으로 보여준다던지, 특정 유저의 데이터만 가져온다던지 등의 기능이 필요하다. 다른 말로 하면 항상 Query는 필요하다.

Query기능을 사용하려면 Partition Key + Sort Key 조합으로 Key를 잡아야 하고, Query 기능은 내가 정한 Partition Key에 속한 Sort Key에 조건을 걸어서 데이터를 가져오는 기능이다.

하나의 Partition Key에 속한 Item의 데이터 총량이 10GB를 넘어서면 안되고, 특정 Partition Key에만 데이터가 몰려서도 안된다. 그러면서 다양한 Query를 하고 싶다는 것은 너무나도 어려운 문제이다.

매력적이지만 쉽지 않은 DB

Key-Value Store로는 DynamoDB만한게 없고, Query를 조금이라도 사용하려고 하면 Partition Key를 어떻게 잡아야 할지 고민이 된다. 그래서 내가 잠정적으로 내린 결론은 복잡한 데이터를 다루는 서비스를 만든다면 RDB로 어쩔수 없이 시작해야하고, 그 안에서 Key-Value Storage가 필요한 부분, 데이터의 복잡도는 적지만 Scaling이 필요한 부분만 DynamoDB로 분리해서 구현하면 좋겠다는 생각이 든다.

그냥 무제한 Scaling이 되는 Key-Value Store로만 생각하기엔 DynamoDB에는 좋은 기능들이 너무나도 많다. 간단하게 나열하자면.

DAX(https://aws.amazon.com/dynamodb/dax/)

DynamoDB와 동일한 인터페이스의 memory cache 서버. 웹서버의 변경 없이 그냥 서버를 킨것으로 10배 이상 빨라질 수 있다.

Stream + Lambda

DynamoDB에 추가/수정/삭제되는 데이터가 생길때마다 Lambda를 실행 할 수 있다. 이 기능을 이용하면 어떤 특정 데이터가 들어왔을때 이메일로 알림을 보낸다던지, ElasticCache같은 검색 엔진에 데이터를 추가해줄 수 있다.

Global Table

전세계를 대상으로 서비스를 한다면 웹서버를 전세계에 분포를 할 순 있지만, DB는 지구 어딘가 한곳에 있어야 한다. DynamoDB의 Global Table 기능을 사용하면 AWS가 지원하는 리젼에 자동으로 동기화되는 데이터를 구성할 수 있다. 이 기능을 사용하면 전세계에 웹서버 뿐만 아니라 DB까지 분산되어 여러 국가에서 쾌적한 서비스를 제공 할 수 있다.

DynamoDB로 만든 VPN 서비스

https://www.veilduck.app

최근에 출시한 VeilDuck VPN(https://www.veilduck.app)의 DB는 DynamoDB를 사용하고 있다. 사용자에게 무료 기간을 주거나, 유료 결제시 발급되는 고유 번호(티켓)을 발급하게 되는데, 이 정보를 보관하는 Key-Value Store로써 활용하고 있다.

--

--