티스토리 뷰

 

이전 글에는 문제가 있다.

 

현재 DDB Table에는 이미 3천개 이상의 데이터가 쌓여있고, 앞으로 꾸준히 데이터가 쌓일 예정이다.

 

그런데 조회 쿼리 ScanCommand를 사용했다. 이 조회 방식은 언젠가 문제를 일으킬 것이다.

 

어떤 문제인지는, AWS의 DDB 스캔 사용 모범사례를 보면 알 수 있다.

일반적으로 Scan 작업은 DynamoDB의 다른 작업보다 비효율적입니다. Scan 작업은 항상 전체 테이블이나 보조 인덱스를 스캔합니다. 그런 후 값을 필터링하여 원하는 결과를 얻기 때문에 결과 세트에서 데이터를 제거해야 하는 단계가 추가됩니다.
가능한 경우, 대용량 테이블 또는 인덱스에서 필터를 사용해 다수의 결과를 제거해야 하는 경우에는 Scan 작업을 최대한 피하는 것이 좋습니다. 여기에 테이블이나 인덱스 용량이 커질수록 Scan 작업 속도가 느려집니다. 

 

요약하면, Scan 작업은 언제나 풀스캔이기 때문에 테이블의 용량이 커질수록 느려지기 때문에 권장하지 않는다는 것이다.

 

그럼 어떤 대안이 있을까?

 

대략적으로 DDB에는 조회에 3가지 방식이 있는 것 같은데, 어떤 것을 사용하는 게 좋을지 조금 짚고 넘어가보자.

(위 글에선 QueryCommand(이하 쿼리 작업)와 사용할 수 있다면, getItem 커맨드도 권장한다.)

 

Scan, Query, GetItem을 언제 사용하는게 적절한지 그려놓은 차트가 있다.

 

그런데 내 상황은 조금 애매하다. 시나리오에 시간 범위가 있기 때문이다.

 

시간범위 조회는 Collection이 될 것이고, 초반에 언급한 것처럼 Scan은 사용할 수 없는 상황이다.

 

그렇다면 쿼리 작업으로 조회를 수행해야하는데, 현재 시나리오에는 제약조건이 하나 있다.

 

조회하는 데이터에 Partition Key가 없다는 점이다.

 

쿼리 작업을 사용하기 위해서 파티션 키가 반드시 필요하다.

 

다행히도, Dynamo DB에는 초기 지정한 파티션 키 외에 별도의 파티션 키를 지정하는 방법이 있는데

 

이 기능이 GSI(Global Secondary Indexes)다.

 

GSI(Global Secondary Indexes) 란?

파티션 키와 정렬 키와 기본 테이블과 다를 수 있는 인덱스입니다. 모든 파티션에서 인덱스의 쿼리가 기본 테이블의 모든 데이터에 적용될 수 있으므로 글로벌 보조 인덱스는 글로벌하게 간주됩니다. 글로벌 보조 인덱스는 기본 테이블과 별개로 자체 파티션 공간에 저장되며 기본 테이블과는 별도로 크기 조정됩니다.

 

말 그대로, 별도의 파티션 키를 지정하는 기능이다.

 

설정 방법은 간단하다.

 

DynamoDB > 테이블 > 업데이트 생성 > 인덱스 > 인덱스 생성
파티션 키로 지정할 컬럼과 정렬 키로 사용할 컬럼을 지정해준다.

 

속성 프로젝션은 GSI가 생성되면 테이블이 복사되는데, 어떤 데이터들을 복사할지 지정하는 기능이다.

 

dev 서버에서는 데이터 양이 적기 때문에 All로 지정했다. 

 

상용으로 넘어갈 때는 include로 별도의 데이터들을 지정할 것 같다.

(지정하는 컬럼 수만큼 복사되어 컬럼이 많을 수록 복사되는 데이터의 크기가 커지고 그에 따라 추가 요금이 부과된다.)

 

이제 코드로 넘어가보자.

 

람다에서 사용할 예정이므로, 자바스크립트로 구현했다.

 

코드

const ddbQuery = async (ddbDocClient, ..., start, end) => {
  let params = {
    TableName: tableName,
    IndexName: 'your gsi name',
    KeyConditionExpression: "gsi expression",
    FilterExpression: "... #time BETWEEN :start AND :end",
    ExpressionAttributeValues: {
      ":start": start,
      ":end": end
    },
    ExpressionAttributeNames: {
      "#time": "time"
    }
  };

  try {
    const result = await ddbDocClient.send(new QueryCommand(params));
    return result.Items;
  } catch (error) {
    console.error('Error querying DynamoDB:', error);
    throw error;
  }
};

ScanCommand에서 사용된  while문을 돌면서, LastEvaluatedKey를 반복 호출하는 부분이 사라지고 하나의 쿼리 작업만 사용된다.

 

조금만 생각해보면 테이블 처음부터 찾는 데이터가 등장할 때까지 조회하는게 말도 안된다는걸 알았을텐데,

 

당시에 너무 급박하게 구현하느라 그때는 미처 고려하지 못했다.

 

... 실제 사용한 데이터를 가리려고 했으니, 별도의 데이터를 삽입하면 된다.

 

추가작업(2014.01.03)

이 코드를 로컬에서 동작시켰을 때는 별 다른 역할 지정이 필요없었다.

 

그런데 람다로 올렸을 때 추가 작업이 필요하다.

 

람다의 역할에 인덱스의 권한도 추가해야한다.

 

역할에 권한 추가하기

{
    "Action": [
        "dynamodb:*"
    ],
    "Resource": [
        "-",
        "-/index/my-index"
    ],
    "Effect": "Allow"
}

 

마치며

DDB를 사실 이렇게까진 알고싶지 않았다.

 

중요한 데이터가 저장되어 있는 RDB에 접근하지 않고 람다와 함께 심플하게 사용하려고 했었기 때문이다.

 

많은 일이 있었지만, 그래도 일단 잠재적으로 발생할 수 있는 문제를 하나 해결했다.

 

하지만 처음에 알고 싶었던 것은 수십, 수백만 데이터가 하나의 DDB 테이블에 쌓였을 때 문제가 없을까? 였다.

 

이 부분에 대해서는 조금 더 조사해봐야 할 것 같다.

 

참고자료

- https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/GSI.html

- https://stackoverflow.com/questions/12241235/dynamodb-query-versus-getitem-for-single-item-retrieval-based-on-the-index

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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
글 보관함