날짜별로 DynamoDB 쿼리


102

관계형 데이터베이스 배경에서 왔으며 Amazon의 DynamoDB로 작업하려고합니다.

해시 키 "DataID"와 범위 "CreatedAt"및 그 안에 많은 항목이있는 테이블이 있습니다.

특정 날짜 이후에 생성되고 날짜별로 정렬 된 모든 항목을 가져 오려고합니다. 관계형 데이터베이스에서는 매우 간단합니다.

DynamoDB에서 내가 찾을 수있는 가장 가까운 것은 필터보다 큰 범위 키를 사용하는 쿼리입니다. 유일한 문제는 쿼리를 수행하려면 목적을 무효화하는 해시 키가 필요하다는 것입니다.

그래서 내가 뭘 잘못하고 있니? 내 테이블 스키마가 잘못 되었습니까? 해시 키가 고유하지 않아야합니까? 아니면 다른 쿼리 방법이 있습니까?

답변:


34

업데이트 된 답변 :

DynamoDB에서는 이러한 종류의 쿼리를 지원하기 위해 보조 인덱스를 지정할 수 있습니다. 보조 인덱스는 글로벌 (인덱스가 해시 키에 걸쳐 전체 테이블에 걸쳐 있음을 의미 함)이거나 인덱스가 각 해시 키 파티션 내에 존재 함을 의미하므로 쿼리를 만들 때 해시 키도 지정되어야 함을 의미합니다.

이 질문의 사용 사례의 경우 "CreatedAt"필드에 글로벌 보조 인덱스를 사용하려고합니다.

DynamoDB 보조 인덱스에 대한 자세한 내용은 보조 인덱스 문서를 참조하십시오.

원래 답변 :

DynamoDB는 범위 키에서만 인덱싱 된 조회를 허용하지 않습니다. 해시 키는 서비스가 데이터를 찾기 위해 조사 할 파티션을 알 수 있도록 필요합니다.

물론 스캔 작업을 수행하여 날짜 값으로 필터링 할 수 있지만 전체 테이블 스캔이 필요하므로 이상적이지 않습니다.

여러 기본 키에서 시간별로 인덱싱 된 레코드 조회를 수행해야하는 경우 DynamoDB가 사용하기에 이상적인 서비스가 아니거나 항목을 저장하기 위해 별도의 테이블 (DynamoDB 또는 관계형 스토어)을 활용해야 할 수 있습니다. 인덱싱 된 조회를 수행 할 수있는 메타 데이터입니다.


14
아래 답변에 대한 의견을 참조하십시오. 적어도 OP가 요청한 것이 아니라 지금 이것을 처리 방법 이 없습니다 . GSI에서는 여전히 해시 키를 지정해야하므로 CreatedAt특정 지점보다 큰 모든 레코드를 쿼리 할 수 ​​없습니다 .
pkaeding

4
@pkaeding이 맞습니다. scan을 사용하여 특정 날짜 보다 오래된 레코드를 가져올 수 있지만 정렬 된 순서로는 가져올 수 없습니다. 이 경우 GSI는 도움이되지 않습니다. 파티션 키 를 정렬 할 수 없으며 범위 키만 쿼리 할 수도 없습니다 .
gkiko

15
혼란스러운 분들을 위해. 이 답변은 틀 렸습니다. 그의 원래 대답은 옳지 만 업데이트 된 대답은 그렇지 않습니다. 아래 Warren Parad의 답변을 읽으십시오. 맞습니다.
라이언 Shillington

1
@MikeBrant 보다 큼 기호를 사용하여 테이블의 GSI 해시 키 (CreatedAt)에있는 테이블 을 쿼리 (스캔이 아니라 테이블의 모든 항목을 확인하므로 매우 비효율적이고 비용이 많이 듭니다)하고 싶습니다. 내가 아는 한 이것은 할 수 없습니다.
아지즈 자 베드

4
날짜주 파티션 으로 사용하는 동안 발생할 수있는 문제 는 대부분의 데이터 저장소에서 새 데이터가 이전 데이터보다 더 자주 쿼리되기 때문에 일부 또는 피어 중 하나에 핫스팟을 만들 수 있다는 것입니다.
Knowledge

53

현재 테이블 구조가 주어지면 현재 DynamoDB에서는 불가능합니다. 큰 도전은 테이블 (파티션)의 해시 키가 별도의 테이블을 만드는 것으로 취급되어야한다는 것을 이해하는 것입니다. 어떤면에서 이것은 정말 강력합니다 (파티션 키를 각 사용자 또는 고객 등에 대한 새 테이블을 만드는 것으로 생각하십시오).

쿼리는 단일 파티션에서만 수행 할 수 있습니다. 그것이 정말로 이야기의 끝입니다. 즉, 날짜별로 쿼리하려는 경우 (epoch 이후 msec를 사용하려는 경우) 단일 쿼리에서 검색하려는 모든 항목은 동일한 해시 (파티션 키)를 가져야합니다.

자격을 갖추어야합니다. scan찾고있는 기준에 따라 절대적으로 할 수 있습니다. 문제는 없지만 테이블의 모든 행을 살펴보고 해당 행에 매개 변수와 일치하는 날짜가 있는지 확인하게됩니다. 특히 처음에 날짜별로 이벤트를 저장하는 경우 (즉, 많은 행이있는 경우) 매우 비쌉니다.

문제를 해결하기 위해 모든 데이터를 단일 파티션에 저장하고 싶을 수 있지만, 각 파티션이 총 세트 양의 일부만 수신하므로 처리량이 매우 낮습니다.

가장 좋은 방법은 데이터를 저장하기 위해 만들 더 유용한 파티션을 결정하는 것입니다.

  • 정말로 모든 행을보아야합니까? 아니면 특정 사용자의 행만보아야합니까?

  • 먼저 월별로 목록을 좁히고 여러 쿼리 (매월 하나씩)를 수행해도됩니까? 아니면 연도 별?

  • 시계열 분석을 수행하는 경우 몇 가지 옵션이 있습니다 PUT. query더 쉽게 하기 위해 파티션 키를 계산 된 것으로 변경 하거나 추가 전용 로깅에 적합한 kinesis와 같은 다른 aws 제품을 사용하십시오.


4
"연도 별"고려에 대해 마지막 단락에서 제시 한 옵션을 강조하고 싶습니다. 이와 같은 속성 yyyy을 만들고 그것에 해시하지만 created범위 키로 사용할 수 있는 날짜 도 만듭니다 . 그런 다음 연간 10GB의 데이터 (하루 27MB)를 얻습니다. 이는 더 많은 상황에 적합합니다. 날짜 쿼리가 연도 경계를 넘을 때 매년 쿼리를 만들어야하지만 적어도 작동하며 더미 해시 키를 만드는 것보다 안전합니다.
Ryan Shillington


1
위의 링크에서 설명했듯이 엄격한 시간 기반 파티션 키는 핫스팟으로 이어질 수 있습니다. 시간 기반 파티션 키를 사용해야하는 경우 파티션 키에 다른 요소를 추가하여 여러 파티션에 걸쳐 기간을 분산하는 것이 좋습니다. 0-n 사이의 접두사를 사용하는 제안을 보았습니다. 여기서 n은 버킷이 분산되어야 할 때마다 파티션 수입니다.
드레스

@RyanShillington 글로벌 보조 인덱스 에는 10GB 제한이 없습니다 . 이 제한은 local secondary index 에만 적용됩니다 .
Simon Forsberg

18

이 문제를 해결하기 위해 제가 따랐던 접근 방식은 아래와 같이 Global Secondary Index를 생성하는 것입니다. 이것이 최선의 접근 방식인지 확실하지 않지만 누군가에게 유용하다면 희망을 갖고 있습니다.

Hash Key                 | Range Key
------------------------------------
Date value of CreatedAt  | CreatedAt

HTTP API 사용자에게 데이터 검색 일 수를 지정하는 제한이 적용되며 기본값은 24 시간입니다.

이렇게하면 항상 HashKey를 현재 날짜로 지정할 수 있으며 RangeKey는 검색하는 동안> 및 <연산자를 사용할 수 있습니다. 이렇게하면 데이터가 여러 샤드에 분산됩니다.


8

해시 키 (기본 정렬)는 고유해야합니다 (다른 사람이 언급 한 것과 같은 범위가없는 경우).

귀하의 경우 테이블을 쿼리하려면 보조 인덱스가 있어야합니다.

|  ID  | DataID | Created | Data |
|------+--------+---------+------|
| hash | xxxxx  | 1234567 | blah |

해시 키는 ID입니다. 보조 인덱스는 다음과 같이 정의됩니다. DataID-Created-index (DynamoDB에서 사용할 이름)

그런 다음 다음과 같은 쿼리를 만들 수 있습니다.

var params = {
    TableName: "Table",
    IndexName: "DataID-Created-index",
    KeyConditionExpression: "DataID = :v_ID AND Created > :v_created",
    ExpressionAttributeValues: {":v_ID": {S: "some_id"},
                                ":v_created": {N: "timestamp"}
    },
    ProjectionExpression: "ID, DataID, Created, Data"
};

ddb.query(params, function(err, data) {
    if (err) 
        console.log(err);
    else {
        data.Items.sort(function(a, b) {
            return parseFloat(a.Created.N) - parseFloat(b.Created.N);
        });
        // More code here
    }
});

기본적으로 쿼리는 다음과 같습니다.

SELECT * FROM TABLE WHERE DataID = "some_id" AND Created > timestamp;

보조 인덱스는 필요한 읽기 / 쓰기 용량 단위를 증가 시키므로이를 고려해야합니다. 읽기와 시간에 비용이 많이 드는 스캔을 수행하는 것보다 훨씬 낫습니다 (내가 믿는 100 개 항목으로 제한됨).

이것이 최선의 방법은 아니지만 RD에 익숙한 사람에게는 (저는 SQL에도 익숙합니다) 생산성을 얻는 가장 빠른 방법입니다. 스키마와 관련하여 제약이 없기 때문에 작동하는 것을 채울 수 있으며 가장 효율적인 방식으로 작업 할 수있는 대역폭이 있으면 상황을 변경할 수 있습니다.


1
제약이 없다고 말하지만이 접근 방식은 최대 10GB의 데이터 (최대 단일 파티션)를 저장할 수 있음을 알아야합니다.
Ryan Shillington

이것은 DataID가 알려진 경우에 접근했을 것입니다. 그러나 여기서 우리는 생성 된 날짜가 일정 이상인 모든 행을 가져와야합니다.
Yasith Prabuddhaka 2018 년

3

'제품 카테고리'ID의 줄을 따라 해시 키를 만든 다음 끝에 고유 ID가 추가 된 타임 스탬프의 조합으로 범위 키를 만들 수 있습니다. 이렇게하면 해시 키를 알 수 있고보다 큼 날짜를 쿼리 할 수 ​​있습니다.


1

동일한 해시 키를 여러 개 가질 수 있습니다. 하지만 다양한 범위 키가있는 경우에만. 파일 형식이라고 생각하면됩니다. 형식이 다른 한 동일한 폴더에 동일한 이름을 가진 2 개의 파일을 가질 수 있습니다. 형식이 같으면 이름이 달라야합니다. 동일한 개념이 DynamoDB의 해시 / 범위 키에 적용됩니다. 해시를 이름으로, 범위를 형식으로 생각하면됩니다.

또한 OP 당시에 이러한 항목이 있었는지 기억하지 못하지만 (그렇지 않다고 생각합니다) 이제는 Local Secondary Index를 제공합니다.

이것에 대한 나의 이해는 이제 전체 스캔을 수행하지 않고도 원하는 쿼리를 수행 할 수 있어야한다는 것입니다. 단점은 테이블 생성시 이러한 인덱스를 지정해야하고 항목을 생성 할 때 비워 둘 수 없다는 것입니다. 또한 추가 처리량 (일반적으로 스캔만큼 많지는 않지만)과 스토리지가 필요하므로 완벽한 솔루션은 아니지만 일부에게는 실행 가능한 대안입니다.

그래도 DynamoDB를 사용하는 기본 방법으로 Mike Brant의 답변을 권장합니다. 그 방법을 직접 사용하십시오. 제 경우에는 ID로 해시 키만있는 중앙 테이블이 있고 쿼리 할 수있는 해시 및 범위가있는 보조 테이블이있는 경우 항목이 코드를 중앙 테이블의 "관심 항목"으로 직접 가리 킵니다. .

보조 인덱스에 대한 추가 데이터는 여기 Amazon의 DynamoDB 설명서에서 찾을 수 있습니다. 에있는 .

어쨌든,이 스레드에서 발생하는 다른 모든 사람에게 도움이되기를 바랍니다.


해시 유형의 AWSDynamoDBKeySchemaElement 'createdAt'과 유형 범위의 AWSDynamoDBKeySchemaElement 'createdAt'이있는 DynamoDB 테이블을 생성하려고했는데 Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code = 0 "(null)"UserInfo라는 오류가 발생했습니다. = {__ type = com.amazon.coral.validate # ValidationException, message = KeySchema의 해시 키와 범위 키 요소 모두 이름이 동일합니다}. 그래서 나는 당신이 말하는 것이 옳다고 생각하지 않습니다.
user1709076

나는 당신이 오해했다고 믿습니다 (나는 내 ​​설명에서 그다지 명확하지 않은 것 같습니다). 테이블에 동일한 이름을 가진 두 개의 다른 속성 (열)을 가질 수 없지만 범위 키로 해시 키를 생성 할 때 범위가 다른 한 모두 동일한 해시를 사용하는 여러 항목을 가질 수 있습니다. 그 반대로. 예 : 해시는 "ID"이고 범위는 "Date"입니다. 날짜가 다른 한 ID "1234"의 인스턴스를 2 개 가질 수 있습니다.
DGolberg 2015

아 D 골드버그! 나는 지금 당신을 얻습니다. 훌륭합니다. 따라서 내 경우에는 항상 '날짜 = x 이후'문자 메시지를 쿼리하고 싶기 때문에 모든 문자 메시지가 동일한 'fake_hash = 1'을 갖도록 설정할 수있는 것 같습니다. 그런 다음 내 query.keyConditionExpression = @ "fake_hash = 1 및 #Date> : val"을 수행합니다. 대단히 감사합니다. 다른 입력이 있으면 항상 동일한 값인 해시를 갖는 것이 이상해 보이기 때문에 기꺼이 들으시겠습니까?
user1709076 2015-12-05

다시 확인해야하지만 해시 전용 테이블에 대한 쿼리를 수행 할 수 있다고 확신합니다. 날짜 / 시간 스탬프를 해시로 사용하는 경우 다음 항목에 기록하는 것이 좋습니다. 날짜 / 시간이 겹칠 가능성을 줄이기 위해 밀리 초 또는 나노 / 마이크로 초 (코드가 기록 할 수있는 가장 작은 시간 단위에 관계없이)와 같이 가능한 가장 짧은 단위입니다. 또한 겹침 가능성을 줄이기 위해 낙관적 잠금을 추가 할 수 있습니다. docs.aws.amazon.com/amazondynamodb/latest/developerguide/… 충돌이있는 경우 다시 시도하기 만하면됩니다.
DGolberg

-11

업데이트 된 답변 처리량을 예측할 수있는 Dynamo DB 쿼리를 사용하여이 작업을 수행하는 편리한 방법은 없습니다. 한 가지 (하위 최적) 옵션은 인공 HashKey 및 CreatedAt과 함께 GSI를 사용하는 것입니다. 그런 다음 HashKey만으로 쿼리하고 ScanIndexForward를 언급하여 결과를 정렬합니다. 자연적인 HashKey (항목의 카테고리 등)를 생각 해낼 수 있다면이 방법이 승자입니다. 반면에 모든 항목에 대해 동일한 HashKey를 유지하면 데이터 세트가 10GB (하나의 파티션)를 초과 할 때 처리량에 영향을 미칩니다.

원래 답변 : 이제 GSI를 사용하여 DynamoDB에서이 작업을 수행 할 수 있습니다. "CreatedAt"필드를 GSI로 만들고 (GT some_date)와 같은 쿼리를 실행합니다. 이러한 종류의 쿼리에 대해 날짜를 숫자 (epoch 이후 밀리 초)로 저장합니다.

자세한 내용은 여기에서 확인할 수 있습니다. 글로벌 보조 인덱스-Amazon DynamoDB : http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.

이것은 매우 강력한 기능입니다. 쿼리는 (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) 조건-Amazon DynamoDB : http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html로 제한됩니다.


31
제가 말할 수있는 한 귀하의 답변이 틀 렸기 때문에 저는 반대 투표를했습니다. 테이블의 기본 키와 마찬가지로 EQ 연산자로만 GSI의 해시 키를 쿼리 할 수 ​​있습니다. 이것이 CreatedAtGSI의 범위 키 여야한다고 암시했다면 해시 키를 선택해야합니다. 그런 다음 시작했던 곳으로 돌아갑니다. GT CreatedAt의 특정 값 에 대해서만 GT를 쿼리 할 수 ​​있기 때문 입니다. 해시 키.
PaF 2014

PaF와 동의했습니다. 생성 시간으로 해시 키와 함께 GSI를 사용하는 것은 OP에서 묻는 질문에 도움이되지 않습니다.
4-8-15-16-23-42
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.