Amazon DynamoDB
개발자 안내서 (API 버전 2012-08-10)

글로벌 보조 인덱스

일부 애플리케이션은 서로 다른 속성을 쿼리 기준으로 사용하여 다양한 유형의 쿼리를 수행해야 할 수 있습니다. 이러한 요구 사항을 지원하기 위해 하나 이상의 글로벌 보조 인덱스를 생성하고 이 인덱스에 대해 Query 요청을 발급할 수 있습니다. 모바일 게임 애플리케이션에서 사용자와 점수를 관리하는 GameScores 테이블을 예로 들어 설명해 보겠습니다. GameScores의 각 항목은 파티션 키(UserId) 및 정렬 키(GameTitle)를 기준으로 식별됩니다. 다음 그림은 테이블에서 항목을 구성하는 방식을 보여 줍니다. (일부 속성만 표시)

이제 각 게임의 최고 점수를 표시하는 순위표 애플리케이션을 작성하려는 경우를 가정하겠습니다. 키 속성(UserId 및 GameTitle)을 지정한 쿼리가 매우 효율적일 수 있지만, 애플리케이션에서 GameTitle만을 기준으로 GameScores에서 데이터를 가져와야 할 경우 Scan 작업을 사용해야 합니다. 테이블에 항목을 추가할수록 전체 데이터 스캔이 느려지고 효율성이 저하되면서 다음과 같은 질문에 응답하기가 어려워집니다.

  • Meteor Blasters 게임의 최고 기록 점수

  • Galaxy Invaders의 최고 점수 기록 보유자

  • 최고 승률

키가 아닌 속성에 대한 쿼리 속도를 높이기 위해 글로벌 보조 인덱스를 만들 수 있습니다. 글로벌 보조 인덱스에는 기본 테이블의 부분 속성이 포함되어 있지만 테이블의 기본 키가 아닌 다른 기본 키를 기준으로 구성됩니다. 인덱스 키는 표의 키 속성을 가질 필요가 없으며 테이블과 동일한 키 스키마도 필요하지 않습니다.

예를 들어 파티션 키가 GameTitle이고 정렬 키가 TopScoreGameTitleIndex라는 글로벌 보조 인덱스를 만들 수 있습니다. 기본 테이블의 기본 키 속성이 언제나 인덱스로 프로젝션되므로 UserId 속성이 함께 표시됩니다. 다음 그림은 GameTitleIndex 인덱스를 나타냅니다.

이제 GameTitleIndex를 쿼리하여 Meteor Blasters의 점수를 쉽게 가져올 수 있습니다. 결과는 정렬 키 값 TopScore로 정렬됩니다. ScanIndexForward 파라미터를 false로 설정하는 경우 결과가 내림차순으로 반환되어 가장 높은 점수가 가장 먼저 반환됩니다.

모든 글로벌 보조 인덱스에는 파티션 키가 있어야 하며 선택 사항으로 정렬 키를 가질 수 있습니다. 인덱스 키 스키마는 기본 테이블 스키마와 다를 수 있으며, 단순 기본 키(파티션 키)가 있는 테이블을 가지고 복합 기본 키(파티션 키 및 정렬 키)가 있는 글로벌 보조 인덱스를 만들거나 그 반대도 가능합니다. 인덱스 키 속성은 기본 테이블의 최상위 문자열, 숫자 또는 이진 속성으로 구성될 수 있으며, 다른 스칼라 형식, 문서 형식, 집합 형식은 허용되지 않습니다.

다른 기본 테이블 속성을 인덱스로 프로젝션할 수도 있습니다. 인덱스를 쿼리할 경우 DynamoDB가 이와 같은 프로젝션 속성을 효율적으로 검색할 수 있습니다. 단, 글로벌 보조 인덱스 쿼리의 경우 기본 테이블에서 속성을 가져올 수 없습니다. 예를 들어 위 다이어그램과 같이 GameTitleIndex를 쿼리한 경우 쿼리가 TopScore 이외의 키가 아닌 속성에는 액세스할 수 없습니다(단, 키 속성 GameTitleUserId는 자동으로 프로젝션됨).

DynamoDB 테이블에서 각 키 값은 고유성이 보장되어야 합니다. 하지만 의 키 값은 고유할 필요가 없습니다. 예를 들어 많은 새로운 사용자들이 도전하지만 0점을 넘지 못하는 특히 어려운 Comet Quest라는 게임이 있다고 가정해 보겠습니다. 다음과 같은 데이터로 게임을 나타낼 수 있습니다.

UserId GameTitle TopScore
123 Comet Quest 0
201 Comet Quest 0
301 Comet Quest 0

이 데이터가 GameScores 테이블에 추가되면 DynamoDB가 이 데이터를 GameTitleIndex에 적용합니다. 그런 다음 GameTitle에 Comet Quest, TopScore에 0을 사용하여 인덱스를 쿼리하면 다음과 같은 데이터가 반환됩니다.

지정된 키 값이 있는 항목만 응답에 나타나며, 이 데이터 집합 내에서는 항목에 특정 순서가 없습니다.

글로벌 보조 인덱스는 해당 키 속성이 실제로 있는 데이터 항목만을 추적합니다. 예를 들어 GameScores 테이블에 새 항목을 하나 더 추가하되 필요한 기본 키 속성만 제공한 경우를 가정하겠습니다.

UserId GameTitle
400 Comet Quest

TopScore 속성이 지정되지 않았기 때문에 DynamoDB가 이 항목을 GameTitleIndex에 적용하지 않습니다. 따라서, 모든 Comet Quest 항목에 대해 GameScores를 쿼리하면 다음 4개 항목이 반환됩니다.

그러나 GameTitleIndex로 유사한 쿼리를 실행할 경우 4개가 아닌 3개 항목이 반환됩니다. 그 이유는 존재하지 않는 TopScore를 보유한 항목은 인덱스로 적용되지 않기 때문입니다.

속성 프로젝션

프로젝션(projection)이란 테이블에서 보조 인덱스로 복사되는 속성 집합을 말합니다. 테이블의 파티션 키와 정렬 키는 항상 인덱스로 프로젝션되지만, 다른 속성을 프로젝션하여 애플리케이션의 쿼리 요건을 지원하는 것도 가능합니다. 따라서 인덱스에 쿼리를 실행할 때는 마치 속성이 자체 테이블에 저장되어 있는 것처럼 Amazon DynamoDB가 프로젝션의 모든 속성에 액세스할 수 있습니다.

보조 인덱스를 생성할 때 인덱스에 프로젝션될 속성을 지정해야 합니다. DynamoDB는 속성 지정을 위해 다음과 같이 세 가지 옵션을 제공합니다.

  • KEYS_ONLY – 인덱스의 각 항목은 테이블 파티션 키 및 정렬 키 값, 그리고 인덱스 키 값으로만 구성됩니다. KEYS_ONLY 옵션은 보조 인덱스의 크기를 최소화합니다.

  • INCLUDEKEYS_ONLY에서 설명한 속성뿐만 아니라 키 외에 다른 속성을 지정하여 보조 인덱스에 추가합니다.

  • ALL – 보조 인덱스에 원본 테이블의 모든 속성이 추가됩니다. 모든 테이블 데이터가 인덱스에 복사되기 때문에 ALL 프로젝션은 보조 인덱스의 크기를 최대화합니다.

위 그림에서 GameTitleIndex에는 프로젝션된 속성이 하나 있습니다. 바로 UserId입니다. 애플리케이션이 쿼리의 GameTitleTopScore를 사용하여 각 게임의 최고 득점자의 UserId를 효과적으로 결정할 수 있지만 최고 득점자의 승패 격차의 최대 비율을 효율적으로 확인하지는 못합니다. 확인하려면 기본 테이블에서 추가 쿼리를 실행하여 각 최고 득점자의 승패를 가져와야 합니다. 이 데이터에서 쿼리를 지원하는 더욱 효율적인 방법은 다음 그림과 같은 속성을 기본 테이블에서 글로벌 보조 인덱스로 프로젝션하는 것입니다.

키가 아닌 속성인 승률은 인덱스로 프로젝션되기 때문에 애플리케이션으로 모든 게임 또는 모든 게임과 사용자 ID 조합에 대해 승률을 확인할 수 있습니다.

속성을 글로벌 보조 인덱스로 프로젝션하려면 프로비저닝 처리량 비용과 스토리지 비용 간 균형을 고려해야 합니다.

  • 지연 시간이 가장 낮은 몇 개의 속성만 액세스해야 할 경우 해당 속성만 글로벌 보조 인덱스로 프로젝션하는 방법을 고려해 볼 수 있습니다. 인덱스가 작을수록 스토리지 비용과 쓰기 비용이 절감됩니다.

  • 애플리케이션이 일부 키가 아닌 속성에 빈번하게 액세스할 경우 해당 속성을 글로벌 보조 인덱스로 프로젝션하는 방법을 고려해야 합니다. 의 추가 스토리지 비용이 빈번한 테이블 스캔 수행으로 발생하는 비용보다 경제적입니다.

  • 키가 아닌 속성의 대부분을 빈번하게 액세스해야 할 경우 이러한 속성 또는 전체 기본 테이블을 글로벌 보조 인덱스로 프로젝션할 수 있습니다. 이 경우 유연성이 극대화되지만 스토리지 비용이 최대 두 배까지 상승할 수 있습니다.

  • 애플리케이션이 테이블에 빈번하게 쿼리하지 않지만 테이블 데이터에 대해 많은 쓰기 또는 업데이트 작업을 수행해야 할 경우 KEYS_ONLY를 프로젝션할 수 있습니다. 크기는 최소화되지만 쿼리 작업에 필요할 경우 계속 사용할 수 있습니다.

글로벌 보조 인덱스 쿼리 작업

Query 작업을 사용하여 글로벌 보조 인덱스에서 하나 이상의 항목에 액세스할 수 있습니다. 쿼리는 사용하려는 인덱스의 이름과 기본 테이블의 이름, 쿼리 결과에 반환할 속성, 적용할 쿼리 조건을 지정해야 합니다. DynamoDB는 결과를 오름차순 또는 내림차순으로 반환할 수 있습니다.

순위표 애플리케이션에 대해 게임 데이터를 요청하는 Query에서 다음과 같은 데이터가 반환된 경우를 가정해 보십시오.

{ "TableName": "GameScores", "IndexName": "GameTitleIndex", "KeyConditionExpression": "GameTitle = :v_title", "ExpressionAttributeValues": { ":v_title": {"S": "Meteor Blasters"} }, "ProjectionExpression": "UserId, TopScore", "ScanIndexForward": false }

이 쿼리에서

  • DynamoDB는 Meteor Blasters의 인덱스 항목을 찾기 위해 GameTitle 파티션 키를 사용하여 GameTitleIndex에 액세스합니다. 이 키가 있는 모든 인덱스 항목은 빠른 검색을 위해 인접한 상태로 저장됩니다.

  • 이 게임에서 DynamoDB는 인덱스를 사용하여 모든 사용자 ID와 이 게임의 최고 점수에 액세스합니다.

  • 결과가 반환되고, ScanIndexForward 파라미터가 false로 설정되어 있으므로 내림차순으로 정렬됩니다.

글로벌 보조 인덱스 스캔

Scan 작업을 사용하여 글로벌 보조 인덱스에서 모든 데이터를 검색할 수 있습니다. 요청에 기본 테이블 이름과 인덱스 이름을 제공해야 합니다. DynamoDB는 Scan을 사용하여 인덱스에 있는 모든 데이터를 읽고 애플리케이션에 반환합니다. 또한 일부 데이터만 반환하고 나머지 데이터를 무시하도록 요청할 수 있습니다. 그러려면 Scan 작업의 FilterExpression 파라미터를 사용합니다. 자세한 내용은 스캔에 대한 필터 표현식 단원을 참조하십시오.

테이블과 글로벌 보조 인덱스 간 데이터 동기화

DynamoDB는 각 글로벌 보조 인덱스를 기본 테이블과 자동으로 동기화합니다. 애플리케이션이 테이블에 항목을 쓰거나 삭제하면 해당 테이블의 가 최종적으로 일관된 모델을 사용하여 비동기적으로 업데이트됩니다. 애플리케이션이 인덱스에 직접 쓰기를 수행하는 경우는 없습니다. 하지만, 가 이러한 인덱스를 유지하는 방식이 어떤 영향을 미치는지 이해하는 것이 중요합니다.

글로벌 보조 인덱스를 생성할 때 하나 이상의 인덱스 키 속성과 해당 데이터 형식을 지정합니다. 다시 말해, 기본 테이블에 항목을 쓸 때마다 해당 속성의 데이터 형식이 인덱스 키 스키마의 데이터 형식과 일치해야 합니다. GameTitleIndex의 경우, 인덱스의 GameTitle 파티션 키가 문자열 데이터 형식으로 정의되고, 인덱스의 TopScore 정렬 키는 숫자 형식입니다. GameScores 테이블에 항목을 추가하고 GameTitle 또는 TopScore에 다른 데이터 형식을 지정할 경우 DynamoDB에서 데이터 형식 불일치로 인한 ValidationException을 반환합니다.

테이블에 항목을 추가하거나 삭제하면 해당 테이블의 글로벌 보조 인덱스가 최종 일관성 방식으로 업데이트됩니다. 정상적인 조건에서 해당 테이블 데이터의 변경 사항은 거의 밀리초 수준으로 에 적용됩니다. 하지만, 간혹 장애가 발생할 경우 긴 적용 지연이 발생할 수 있습니다. 따라서, 애플리케이션은 의 쿼리가 최신이 아닌 결과를 반환할 수 있음을 예상하고 그러한 상황을 처리할 수 있어야 합니다.

테이블에 항목을 쓸 경우 모든 글로벌 보조 인덱스 정렬 키에 대해 속성을 지정하지 않아도 됩니다. 예를 들어 GameTitleIndex의 경우, GameScores 테이블에 새 항목을 쓰기 위해 TopScore 속성의 값을 지정할 필요가 없습니다. 이 경우 Amazon DynamoDB는 이 특정 항목의 인덱스에 데이터를 쓰지 않습니다.

글로벌 보조 인덱스가 많은 테이블은 인덱스가 적은 테이블보다 쓰기 작업에 더 높은 비용이 발생합니다. 자세한 내용은 글로벌 보조 인덱스 프로비저닝 처리량 모델 고려 사항 단원을 참조하십시오.

글로벌 보조 인덱스 프로비저닝 처리량 모델 고려 사항

글로벌 보조 인덱스를 생성할 때는 해당 인덱스에 예상되는 워크로드에 대한 읽기 및 쓰기 용량 단위를 지정해야 합니다. 글로벌 보조 인덱스의 프로비저닝 처리량 설정 값은 기본 테이블의 설정 값과 별개입니다. 글로벌 보조 인덱스의 Query 작업은 기본 테이블이 아닌 인덱스에서 읽기 용량 단위를 소비합니다. 테이블에 항목을 추가하거나 삭제하면 해당 테이블의 글로벌 보조 인덱스도 업데이트됩니다. 이러한 인덱스 업데이트는 기본 테이블이 아닌 인덱스에서 쓰기 용량 단위를 소비합니다.

예를 들어 글로벌 보조 인덱스를 Query하고 프로비저닝된 읽기 용량을 초과할 경우 요청에 병목 현상이 발생합니다. 테이블에 많은 쓰기 작업을 수행할 때 해당 테이블의 에 충분한 쓰기 용량이 없을 경우 테이블의 쓰기 작업에 병목 현상이 발생합니다.

참고

잠재적 조절을 피하기 위해서 글로벌 보조 인덱스용 프로비저닝된 쓰기 용량은 새로운 업데이트로 기본 테이블과 글로벌 보조 인덱스가 모두 쓰여지기 때문에 기본 테이블의 쓰기 용량 이상이어야 합니다.

글로벌 보조 인덱스에 프로비저닝된 처리량 설정을 보려면 DescribeTable 작업을 사용합니다. 테이블의 모든 글로벌 보조 인덱스에 대한 세부 정보가 반환됩니다.

읽기 용량 유닛

Global secondary index는 Eventually Consistent Read를 지원하며, 각각 읽기 용량 단위의 50%를 소비합니다. 즉, 단일 글로벌 보조 인덱스 쿼리는 읽기 용량 단위당 최대 2 × 4 KB = 8KB까지 검색할 수 있습니다.

글로벌 보조 인덱스 쿼리의 경우 DynamoDB는 테이블에 대한 쿼리와 같은 방식으로 프로비저닝된 읽기 작업을 계산합니다. 유일한 차이는 기본 테이블 항목의 크기가 아닌 인덱스 항목의 크기를 기준으로 계산이 이루어진다는 점입니다. 읽기 용량 단위의 수는 반환된 모든 항목에서 프로젝션된 모든 속성 크기의 합계입니다. 그런 다음 결과가 다음 경계로 반올림됩니다. DynamoDB에서 프로비저닝된 처리량을 계산하는 방법에 대한 자세한 내용은 읽기 및 쓰기 처리량 설정 단원을 참조하십시오.

Query 작업에서 반환하는 결과의 최대 크기는 1 MB이며, 여기에는 반환된 모든 항목의 모든 속성 이름 및 값의 크기가 포함됩니다.

예를 들어 각 항목에 2000바이트의 데이터가 포함된 글로벌 보조 인덱스를 가정해 보겠습니다. 이제 이 인덱스를 Query했을 때 쿼리에서 8개 항목을 반환한 경우를 가정해 보십시오. 일치하는 항목의 총 크기는 2000바이트 × 8개 항목 = 16,000바이트이며, 이 값은 가장 근접한 4 KB 경계로 반올림됩니다. 글로벌 보조 인덱스 쿼리는 최종적으로 일관되므로 총 비용은 0.5 × (16KB/4 KB), 즉 2 읽기 용량 단위입니다.

쓰기 용량 유닛

테이블에 항목을 추가, 업데이트 또는 삭제하고 이로 인해 글로벌 보조 인덱스가 영향을 받을 경우 글로벌 보조 인덱스에서 작업에 프로비저닝된 쓰기 용량 단위를 소비합니다. 쓰기의 총 프로비저닝된 처리량 비용은 기본 테이블에 쓰기 작업으로 소비된 쓰기 용량 단위와 글로벌 보조 인덱스를 업데이트하여 소비된 쓰기 용량 단위의 합계로 구성됩니다. 글로벌 보조 인덱스 업데이트가 필요하지 않은 테이블 쓰기의 경우 인덱스에서 쓰기 용량이 소비되지 않습니다.

테이블 쓰기가 성공하기 위해서는 테이블 및 모든 글로벌 보조 인덱스의 프로비저닝 처리량 설정에 쓰기를 수용할 수 있을 만큼 충분한 쓰기 용량이 있어야 합니다. 그렇지 않을 경우 테이블에 대한 쓰기에 병목 현상이 발생합니다.

글로벌 보조 인덱스에 항목을 쓰는 비용은 몇 가지 요인에 따라 달라집니다.

  • 인덱싱된 속성을 정의하는 테이블에 새 항목을 쓰거나 이전에 정의되지 않은 인덱싱된 속성을 정의하도록 기존 항목을 업데이트하는 경우 항목을 인덱스에 넣으려면 1번의 쓰기 작업이 필요합니다.

  • 테이블을 업데이트하여 인덱스 키 속성 값이 A에서 B로 변경될 경우 두 번의 쓰기, 즉, 인덱스에서 이전 항목을 삭제하는 쓰기와 새 항목을 인덱스에 추가하는 쓰기가 필요합니다. 

  • 항목이 인덱스에 있지만 테이블 쓰기로 인해 인덱스 속성이 삭제된 경우 인덱스에서 기존 항목 프로젝션을 삭제하는 한 번의 쓰기가 필요합니다.

  • 항목이 업데이트되기 전후에 인덱스에 항목이 없을 경우 인덱스에 추가 쓰기 비용이 발생하지 않습니다.

  • 테이블을 업데이트하여 인덱스 키 스키마에 프로젝션된 속성 값이 변경되었지만 인덱스 키 속성 값이 변경되지 않은 경우 인덱스에 프로젝션된 속성 값을 업데이트하는 한 번의 쓰기가 필요합니다.

이러한 모든 요인은 인덱스에 있는 각 항목의 크기가 쓰기 용량 단위를 계산하기 위한 1 KB 항목 크기보다 작거나 같은 경우를 가정합니다. 이보다 큰 인덱스 항목에서는 추가 쓰기 용량 단위가 필요합니다. 쿼리에서 어떤 속성을 반환해야 하는지 고려하고 해당 속성만 인덱스에 프로젝션함으로써 쓰기 비용을 최소화할 수 있습니다.

글로벌 보조 인덱스 스토리지 고려 사항

애플리케이션에서 테이블에 항목을 쓰는 경우 DynamoDB는 해당 속성이 표시되어야 하는 모든 글로벌 보조 인덱스에 올바른 속성 하위 집합을 자동으로 복사합니다. AWS 계정에는 기본 테이블의 항목 스토리지 비용 및 해당 테이블에 있는 글로벌 보조 인덱스의 속성 스토리지 비용이 청구됩니다.

인덱스 항목에서 사용하는 공간의 양은 다음의 합계입니다.

  • 기본 테이블 기본 키(파티션 및 정렬 키)의 크기(바이트)

  • 인덱스 키 속성의 크기(바이트)

  • 프로젝션된 속성(있는 경우)의 크기(바이트)

  • 인덱스 항목당 오버헤드의 100 bytes

글로벌 보조 인덱스의 스토리지 요구 사항을 추정하려면 인덱스의 평균 항목 크기를 추정한 다음 기본 테이블에서 글로벌 보조 인덱스 키 속성이 있는 항목의 수를 곱합니다.

테이블에 특정 속성이 정의되지 않은 항목이 포함되어 있지만 해당 속성이 인덱스 파티션 키 또는 정렬 키로 정의된 경우 DynamoDB에서 해당 항목의 데이터를 인덱스에 쓰지 않습니다.