DynamoDB란? AWS에서 개발한 key-value형 NoSQL 데이터베이스
사용하면서 느꼈었던 장점
별도로 설치나 버전 관리 등을 할 일이 없어서 편리 했었다.
저장 공간 용량이 제한이 없기 때문에 신경 안 썼었다.
사용량 만큼 요금이 부과 된다.
사용하면서 느꼈었던 단점
여러 데이터를 삽입할 때 (bulk insert) 개수 제한이 있어서 조금 불편 했었다.
보조 인덱스 생성 시 데이터 수가 많지 않은데 꽤 느린 편이다.
테이블 DynamoDB를 사용 하기전에 테이블이 존재 하기 때문에 생성을 해 줘야 한다.
웹 콘솔 기준 테이블 작성 시 나오는 항목을 아는 대로 정리를 해 본다.
테이블은 한 번 만들면 수정이 안 되므로 어떤 데이터를 다룰지에 따라 구성이 다르기 때문에
신중히 생각을 한 다음 만들어야 한다.
테이블 이름 말 그대로 해당 테이블의 이름을 작성 해야 한다.
테이블명은 해당 리전에서는 중복된 이름으로는 만들 수 없기 때문에 고유해야 한다.
예료 서울 리전에서 users란 테이블을 생성 시 서울에서는 user란 테이블을 새로 못 만든다.
서울 리전에서 users가 있지만 다른 리전(도쿄)같은 경우에는 users 테이블 생성 가능
기본 키 (파티션 키) DynamoDB는 key-value의 구조다.
기본 키는 key에 해당 하기 때문에 고유한 데이터가 들어올 항목으로 만들어야 한다.
데이터를 삽입 할 때 필수적으로 들어 가야 한다.
예로 이전에 쓰던 서비스에서는 유저 테이블을 생성 시 기본 키는 user_id로 만들었었다.
정렬 키 정렬 키는 어떠한 데이터를 다룰 지에 따라 선택 / 미선택을 하면 된다.
내가 느끼기엔 second key 같은 느낌인데 예로 유저의 행동을 모두 기록하는 테이블을 생성 시
기본 키만 생성한 경우에는 user_id가 a의 행동은 1회만 삽입을 할 수 있다.
왜냐면 다음 번 삽입 시 user_id에 a가 등록되어 있기 때문이다.
이러한 경우에 정렬 키를 추가하여 사용하면 가능 한데 예로 정렬 키를 timestamp로 만들어서
삽입을 하면 아래의 테이블처럼 사용을 할 수 있게 된다.
user_id(기본 키)
timestamp(정렬 키)
action
a
1
‘login’
a
2
‘logout’
대신 정렬 키를 추가 하게 되면 이후 삽입, 조회, 삭제 요청 시 정렬 키의 데이터를 필수적으로
넣어야 한다. 그러므로 꼭 필요한 경우에만 구성에 포함 하자.
보조 인덱스 key-value 구조이기 때문에 기본적으로 조회 시 기본 키 또는 기본 + 정렬 키로만 검색이
가능한데 예로 아래의 테이블에서 유저의 login한 액션만 조회를 하고 싶을 때
보조 인덱스를 추가해서 이용을 하면 된다.
user_id(기본 키)
timestamp(정렬 키)
action
a
1
‘login’
a
2
‘logout’
b
3
‘login’
보조 인덱스도 마찬가지로 기본 키 그리고 선택 사항으로 정렬 키를 추가할 수 있다.
읽기/쓰기 용량 모드 읽기 용량 / 쓰기 용량 설정에 따라 사용 가격의 구성이 정해 진다.
이 용량은 사용 회수라 생각을 하고 설정을 했었다. 유저가 많아져서 사용자가 많아 지면
훅 올라가고, 사용자가 떨어 지면 자연스레 내려가고…
읽기 용량은 올려도 가격이 많이 변화가 없는데 쓰기 용량은 좀 만 올려도 가격이 뻥튀기 된다.
처음 DynamoDB 사용할 땐 온디멘드 모드가 없어서 용량 크기에 좀 불안 했었는데
추가가 되어서 서비스 때 변경을 했었다. 온디멘드 최고!
오토스케일링 사용 수가 많아지면 설정에 따라 알아서 늘어나고 하는데 이 부분은 크게 신경을 안 써봤다.
온디멘드라면 건드릴 일도 없으니 패스
CRUD Node.js로 진행해보겠다.
해당 프로젝트 폴더에서 npm을 통해 aws-sdk를 다운 받는다.
Create Put 하나의 row를 삽입 할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Item: { user_id: 'a' , ts: Date .now(), action: 'login' , }, }; dynamo.put(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
BatchWrite 한 번에 row를 여러 개 넣을 수 있는 방법, 그러나 25개 이하만 가능 하다..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { RequestItems: { users: [ { PutRequest: { Item: { user_id: 'b' , ts: Date .now(), action: 'logout' , }, }, }, { PutRequest: { Item: { user_id: 'a' , ts: Date .now(), action: 'charge' , }, }, }, { PutRequest: { Item: { user_id: 'd' , ts: Date .now(), action: 'login' , }, }, } ], }, }; dynamo.batchWrite(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Read Get 하나의 row를 가지고 온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Key: { user_id: 'a' , ts: 1597722895588 , }, }; dynamo.get(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
BatchGet 한 번에 여러 row를 가지고 올 수 있는 방법, 이 역시 개수 제한 100개다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { RequestItems: { users: { Keys: [ { user_id: 'a' , ts: 1597722895588 , }, { user_id: 'b' , ts: 1597723408876 , } ], }, }, }; dynamo.batchGet(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Query 조건을 넣어서 검색을 할 수 있다.
기본 키의 경우에는 equal(=)만 되고 정렬 키가 있는 경우 부등호(>, >= 등등)가 가능 하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , KeyConditionExpression: '#x = :x and #y > :y' , ExpressionAttributeNames: { '#x' : 'user_id' , '#y' : 'ts' , }, ExpressionAttributeValues: { ':x' : 'a' , ':y' : 10000 , } }; dynamo.query(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
보조인덱스를 이용한 쿼리 위에 적은 거 처럼 action을 보조 인덱스로 만들어서 검색을 해 본다.
기본 키는 action 정렬 키는 생략 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , IndexName: 'action-index' , KeyConditionExpression: '#x = :x' , ExpressionAttributeNames: { '#x' : 'action' , }, ExpressionAttributeValues: { ':x' : 'login' , } }; dynamo.query(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Scan 데이터를 모두 검색 할 수 있는 방법 혹여나 해서 Filter는 검색하기 위한 조건이 아닌
모든 데이터를 읽은 후에 조건대로 걸러지는 것이기 때문에 검색 속도와는 전혀 상관이 없다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , }; dynamo.scan(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Update 데이터를 수정하는 방법, 기본 키와 정렬 키를 제외한 항목만 수정이 가능 하다.
UpdateExpression 설정에 따라 조금 다르다.
Set 흔히 쓰는 기존의 X 값을 Y 값으로 변경할 때 쓴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Key: { user_id: 'a' , ts: 1597723408876 }, UpdateExpression: 'set #x = :x' , ExpressionAttributeNames: { '#x' : 'action' }, ExpressionAttributeValues: { ':x' : 10 }, ReturnValues: 'ALL_NEW' , }; dynamo.update(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
ADD 수정할 항목이 숫자 값이고 N만큼 증가시킬 필요가 있을 때 사용 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Key: { user_id: 'a' , ts: 1597723408876 }, UpdateExpression: 'add #x :x' , ExpressionAttributeNames: { '#x' : 'action' }, ExpressionAttributeValues: { ':x' : 1 }, ReturnValues: 'ALL_NEW' , }; dynamo.update(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Remove 특정 항목을 삭제할 때 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Key: { user_id: 'a' , ts: 1597723408876 }, UpdateExpression: 'remove #x' , ExpressionAttributeNames: { '#x' : 'action' }, ReturnValues: 'ALL_NEW' , }; dynamo.update(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
Delete Delete 하나의 row를 삭제할 때 쓴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TableName: 'users' , Key: { user_id: 'a' , ts: 1597723408876 }, }; dynamo.delete(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
BatchWrite 한 번에 여러 row를 삭제하는 방법, 마찬가지로 개수 제한 25개
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { RequestItems: { users: [ { DeleteRequest: { Key: { user_id: 'a' , ts: 1597722895588 , }, }, }, { DeleteRequest: { Key: { user_id: 'b' , ts: 1597723408876 , }, }, } ], }, }; dynamo.batchWrite(params).promise().then((result ) => { console .log(result); }).catch((e ) => { console .log('err' , e); });
트랜잭션 DynamoDB를 처음 사용 했을 때는 못 봤었는데 얼마 안 되어서 추가가 되었다.
실무에서는 써 보진 않음…😅
TransactGetItems, TransactWriteItems 2 가지가 존재 하는데
전자는 읽기 관련 트랜잭션, 후자는 쓰기, 수정, 삭제와 관련된 트랜잭션을 걸 수 있다.
TransactGetItems 유저 테이블에서 유저 정보를 읽어 온 후 유저가 보유한 아이템 테이블을 읽어보려 한다.
유저 테이블에서는 user_id가 문자열인데 현재 데이터 타입이 숫자라서 다르기 때문에
2번째는 실행이 되질 않고 트랜잭션 실패라는 에러를 볼 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const getParams = { TransactItems: [ { Get: { TableName: 'users' , Key: { user_id: 1 , } } }, { Get: { TableName: 'items' , Key: { user_id: 1 , } } }, ], } dynamo.transactGet(getParams).promise().then((r ) => { console .log(JSON .stringify(r, null , 2 )); }).catch((e ) => { console .log(e); });
TransactWriteItems 가입 시 유저 정보를 기록 하고 아이템 테이블에 초기 값으로 포션을 지급하려 한다.
위와 마찬가지로 유저 테이블에서는 user_id가 문자열인데 현재 데이터 타입이 숫자라서
다르기 때문에 2번째는 실행이 되질 않고 트랜잭션 실패라는 에러를 볼 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const AWS = require ('aws-sdk' );AWS.config.update({ region: 'ap-northeast-2' , accessKeyId: 'IAM 엑세스 키' , secretAccessKey: 'IAM 시크릿 엑세스 키' , }); const dynamo = new AWS.DynamoDB.DocumentClient();const params = { TransactItems: [ { Put: { TableName: 'users' , Item: { user_id: 1 , } } }, { Put: { TableName: 'items' , Item: { user_id: 1 , potion: 10 , }, } }, ], } dynamo.transactWrite(params).promise().then((r ) => { console .log(r); }).catch((e ) => { console .log(e); });
참고