View a markdown version of this page

보조 인덱스 사용 - AWS SDK for Java 2.x

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

보조 인덱스 사용

테이블의 기본 키는 DynamoDB가 기본적으로 항목을 저장하고 검색하는 방법을 결정하는 기본 인덱스를 정의합니다.

보조 인덱스는 쿼리 및 스캔 작업에 사용하는 대체 키를 제공합니다. 글로벌 보조 인덱스(GSI)에는 파티션 키와 기본 테이블의 정렬 키와 다를 수 있는 선택적 정렬 키가 있습니다. 반면 로컬 보조 인덱스(LSI)는 기본 인덱스의 파티션 키를 공유하지만 다른 정렬 키를 정의합니다.

글로벌 및 로컬 보조 인덱스에 대한 키 정의

보조 인덱스에 속하는 속성에는 @DynamoDbSecondaryPartitionKey 또는 @DynamoDbSecondarySortKey 주석이 필요합니다.

다음 클래스는 두 인덱스에 대한 주석을 보여줍니다. SubjectLastPostedDateIndex라는 GSI는 파티션 키에 Subject 속성을 사용하고 정렬 키에는 LastPostedDateTime 속성을 사용합니다. ForumLastPostedDateIndex라는 이름의 LSI는 ForumName를 파티션 키로, LastPostedDateTime를 정렬 키로 사용합니다.

Subject 속성은 이중 역할을 한다는 점에 유의하세요. 기본 키의 정렬 키이자 SubjectLastPostedDateIndex라는 GSI의 파티션 키입니다.

MessageThread 클래스

MessageThread 클래스는 Amazon DynamoDB 개발자 안내서예제 스레드 테이블의 데이터 클래스로 사용하기에 적합합니다.

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.util.List;
@DynamoDbBean public class MessageThread { private String ForumName; private String Subject; private String Message; private String LastPostedBy; private String LastPostedDateTime; private Integer Views; private Integer Replies; private Integer Answered; private List<String> Tags; @DynamoDbPartitionKey public String getForumName() { return ForumName; } public void setForumName(String forumName) { ForumName = forumName; } // Sort key for primary index and partition key for GSI "SubjectLastPostedDateIndex". @DynamoDbSortKey @DynamoDbSecondaryPartitionKey(indexNames = "SubjectLastPostedDateIndex") public String getSubject() { return Subject; } public void setSubject(String subject) { Subject = subject; } // Sort key for GSI "SubjectLastPostedDateIndex" and sort key for LSI "ForumLastPostedDateIndex". @DynamoDbSecondarySortKey(indexNames = {"SubjectLastPostedDateIndex", "ForumLastPostedDateIndex"}) public String getLastPostedDateTime() { return LastPostedDateTime; } public void setLastPostedDateTime(String lastPostedDateTime) { LastPostedDateTime = lastPostedDateTime; } public String getMessage() { return Message; } public void setMessage(String message) { Message = message; } public String getLastPostedBy() { return LastPostedBy; } public void setLastPostedBy(String lastPostedBy) { LastPostedBy = lastPostedBy; } public Integer getViews() { return Views; } public void setViews(Integer views) { Views = views; } @DynamoDbSecondaryPartitionKey(indexNames = "ForumRepliesIndex") public Integer getReplies() { return Replies; } public void setReplies(Integer replies) { Replies = replies; } public Integer getAnswered() { return Answered; } public void setAnswered(Integer answered) { Answered = answered; } public List<String> getTags() { return Tags; } public void setTags(List<String> tags) { Tags = tags; } public MessageThread() { this.Answered = 0; this.LastPostedBy = ""; this.ForumName = ""; this.Message = ""; this.LastPostedDateTime = ""; this.Replies = 0; this.Views = 0; this.Subject = ""; } @Override public String toString() { return "MessageThread{" + "ForumName='" + ForumName + '\'' + ", Subject='" + Subject + '\'' + ", Message='" + Message + '\'' + ", LastPostedBy='" + LastPostedBy + '\'' + ", LastPostedDateTime='" + LastPostedDateTime + '\'' + ", Views=" + Views + ", Replies=" + Replies + ", Answered=" + Answered + ", Tags=" + Tags + '}'; } }

인덱스 만들기

Java용 SDK 버전 2.20.86부터 이 createTable() 메서드는 데이터 클래스 주석에서 보조 인덱스를 자동으로 생성합니다. 기본적으로 기본 테이블의 모든 속성이 인덱스에 복사되며 프로비저닝된 처리량 값은 20 읽기 용량 단위 및 20 쓰기 용량 단위입니다.

단, SDK 2.20.86 이전 버전을 사용하는 경우에는 다음 예와 같이 테이블과 함께 인덱스를 빌드해야 합니다. 이 예제에서는 Thread 테이블의 인덱스 두 개를 빌드합니다. 빌더 매개 변수에는 주석 줄 1과 2 다음에 표시된 것처럼 두 가지 유형의 인덱스를 모두 구성하는 메서드가 있습니다. 인덱스 빌더의 indexName() 메서드를 사용하여 데이터 클래스 주석에 지정된 인덱스 이름을 의도한 인덱스 유형과 연결할 수 있습니다.

이 코드는 모든 테이블 속성이 주석 라인 3과 4 다음에 있는 두 인덱스 모두에 포함되도록 구성합니다. 속성 프로젝션에 대한 자세한 내용은 Amazon DynamoDB 개발자 안내서에서 확인할 수 있습니다.

public static void createMessageThreadTable(DynamoDbTable<MessageThread> messageThreadDynamoDbTable, DynamoDbClient dynamoDbClient) { messageThreadDynamoDbTable.createTable(b -> b // 1. Generate the GSI. .globalSecondaryIndices(gsi -> gsi.indexName("SubjectLastPostedDateIndex") // 3. Populate the GSI with all attributes. .projection(p -> p .projectionType(ProjectionType.ALL)) ) // 2. Generate the LSI. .localSecondaryIndices(lsi -> lsi.indexName("ForumLastPostedDateIndex") // 4. Populate the LSI with all attributes. .projection(p -> p .projectionType(ProjectionType.ALL)) ) );

인덱스를 사용하여 쿼리

다음 예시에서는 로컬 보조 인덱스 ForumLastPostedDateIndex를 쿼리합니다.

주석 줄 2에 이어 DynamoDbIndex.query() 메서드를 호출할 때 필요한 QueryConditional 객체를 생성합니다.

인덱스 이름을 전달하여 주석 줄 3 다음에 쿼리하려는 인덱스에 대한 참조를 얻습니다. 주석 줄 4에 이어 QueryConditional 객체를 전달하는 인덱스에서 query() 메서드를 호출합니다.

또한 주석 줄 5 다음에 표시된 대로 세 가지 속성 값을 반환하도록 쿼리를 구성합니다. attributesToProject()가 호출되지 않은 경우 쿼리는 모든 속성 값을 반환합니다. 지정된 속성 이름은 소문자로 시작하는 것을 알 수 있습니다. 이러한 속성 이름은 테이블에 사용된 이름과 일치하지만 반드시 데이터 클래스의 속성 이름과 일치할 필요는 없습니다.

주석 줄 6 다음에 결과를 반복하고 쿼리에서 반환된 각 항목을 기록하고 목록에 저장하여 호출자에게 반환합니다.

public class IndexScanExamples { private static Logger logger = LoggerFactory.getLogger(IndexScanExamples.class); public static List<MessageThread> queryUsingSecondaryIndices(String lastPostedDate, DynamoDbTable<MessageThread> threadTable) { // 1. Log the parameter value. logger.info("lastPostedDate value: {}", lastPostedDate); // 2. Create a QueryConditional whose sort key value must be greater than or equal to the parameter value. QueryConditional queryConditional = QueryConditional.sortGreaterThanOrEqualTo(qc -> qc.partitionValue("Forum02").sortValue(lastPostedDate)); // 3. Specify the index name to query. final DynamoDbIndex<MessageThread> forumLastPostedDateIndex = threadTable.index("ForumLastPostedDateIndex"); // 4. Perform the query using the QueryConditional object. final SdkIterable<Page<MessageThread>> pagedResult = forumLastPostedDateIndex.query(q -> q .queryConditional(queryConditional) // 5. Request three attribute in the results. .attributesToProject("forumName", "subject", "lastPostedDateTime")); List<MessageThread> collectedItems = new ArrayList<>(); // 6. Iterate through pages response and sort the items. pagedResult.stream().forEach(page -> page.items().stream() .sorted(Comparator.comparing(MessageThread::getLastPostedDateTime)) .forEach(mt -> { // 7. Log the returned items and add the collection to return to the caller. logger.info(mt.toString()); collectedItems.add(mt); })); return collectedItems; }

쿼리가 실행되기 전의 데이터베이스에는 다음과 같은 항목이 있습니다.

MessageThread{ForumName='Forum01', Subject='Subject01', Message='Message01', LastPostedBy='', LastPostedDateTime='2023.03.28', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject02', Message='Message02', LastPostedBy='', LastPostedDateTime='2023.03.29', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject04', Message='Message04', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject08', Message='Message08', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject10', Message='Message10', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject03', Message='Message03', LastPostedBy='', LastPostedDateTime='2023.03.30', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject06', Message='Message06', LastPostedBy='', LastPostedDateTime='2023.04.02', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum03', Subject='Subject09', Message='Message09', LastPostedBy='', LastPostedDateTime='2023.04.05', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum05', Subject='Subject05', Message='Message05', LastPostedBy='', LastPostedDateTime='2023.04.01', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum07', Subject='Subject07', Message='Message07', LastPostedBy='', LastPostedDateTime='2023.04.03', Views=0, Replies=0, Answered=0, Tags=null}

1행과 6행의 로깅 명령문을 실행하면 다음과 같은 콘솔 출력이 나타납니다.

lastPostedDate value: 2023.03.31 MessageThread{ForumName='Forum02', Subject='Subject04', Message='', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject08', Message='', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null} MessageThread{ForumName='Forum02', Subject='Subject10', Message='', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}

쿼리가 Forum02forumName 값과 2023.03.31보다 크거나 같은 lastPostedDateTime을 반환했습니다. 인덱스에 message 속성 값이 있더라도 결과에는 빈 문자열이 있는 message 값이 표시됩니다. 이는 주석 줄 5 이후에 코드로 메시지 속성이 프로젝션되지 않았기 때문입니다.

글로벌 보조 인덱스(GSI)에 복합 키 사용

Java 2.x DynamoDB용 SDK 향상된 클라이언트는 글로벌 보조 인덱스에 대한 복합 키를 지원합니다. 단일 GSI에 대해 최대 4개의 파티션 키 속성과 4개의 정렬 키 속성을 정의할 수 있습니다. 이렇게 하면 여러 속성을 클라이언트 측의 단일 문자열 키로 연결할 필요가 없습니다.

@DynamoDbSecondaryPartitionKey@DynamoDbSecondarySortKey 주석의 order 파라미터를 사용하여 복합 키에서 각 속성의 위치를 지정합니다. order 파라미터는 열거Order형(향상된 DynamoDB 클라이언트의 일부) FIRST, SECOND, 및 THIRD의 값을 허용합니다FOURTH.

복합 키 주석으로 데이터 클래스 주석 달기

다음 예제에서는 주문 테이블을 모델링합니다. 기본 테이블은를 파티션 키orderId로 사용합니다. OrdersByStatusDateAmount라는 GSI는 customerId 및의 복합 파티션 키status와 및 orderDate의 복합 정렬 키를 사용합니다amount. 이 설계를 사용하면 상태, 날짜 및 금액 임계값을 기준으로 필터링된 고객의 주문을 쿼리할 수 있습니다.

import software.amazon.awssdk.enhanced.dynamodb.mapper.Order; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
@DynamoDbBean public class OrderItem { private String orderId; private String customerId; private String status; private String orderDate; private Integer amount; @DynamoDbPartitionKey public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } // First partition key attribute for the GSI. @DynamoDbSecondaryPartitionKey(indexNames = "OrdersByStatusDateAmount", order = Order.FIRST) public String getCustomerId() { return customerId; } public void setCustomerId(String customerId) { this.customerId = customerId; } // Second partition key attribute for the GSI. @DynamoDbSecondaryPartitionKey(indexNames = "OrdersByStatusDateAmount", order = Order.SECOND) public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } // First sort key attribute for the GSI. @DynamoDbSecondarySortKey(indexNames = "OrdersByStatusDateAmount", order = Order.FIRST) public String getOrderDate() { return orderDate; } public void setOrderDate(String orderDate) { this.orderDate = orderDate; } // Second sort key attribute for the GSI. @DynamoDbSecondarySortKey(indexNames = "OrdersByStatusDateAmount", order = Order.SECOND) public Integer getAmount() { return amount; } public void setAmount(Integer amount) { this.amount = amount; } }

복합 키 인덱스를 사용하여 쿼리

복합 키 인덱스를 쿼리할 때는 빌더에서 addPartitionValue()addSortValue() 메서드를 사용합니다. 주석에 정의된 값과 동일한 순서로 Order 값을 추가합니다. 모든 파티션 키가 필요합니다. 정렬 키는 선택 사항이며 왼쪽에서 오른쪽으로 제공할 수 있습니다.

QueryConditional 객체를 생성하여 쿼리 조건을 지정합니다. keyEqualTo 정확한 일치 또는 , sortGreaterThan, 또는와 같은 정렬 키 조건에 sortLessThan를 사용합니다sortBetweensortBeginsWith. 조건은 마지막으로 제공한 정렬 키에 적용됩니다.

다음 예제에서는 OrdersByStatusDateAmount GSI를 쿼리하여 정렬 키를 점진적으로 추가하여 결과를 좁힙니다.

DynamoDbIndex<OrderItem> index = orderTable.index("OrdersByStatusDateAmount"); // All PENDING orders for customer-123. SdkIterable<Page<OrderItem>> allPending = index.query(q -> q .queryConditional(QueryConditional.keyEqualTo(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING")))); // PENDING orders on a specific date. SdkIterable<Page<OrderItem>> pendingOnDate = index.query(q -> q .queryConditional(QueryConditional.keyEqualTo(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING") .addSortValue("2025-11-04")))); // PENDING orders on a specific date with amount greater than 100. SdkIterable<Page<OrderItem>> filtered = index.query(q -> q .queryConditional(QueryConditional.sortGreaterThan(k -> k .addPartitionValue("customer-123") .addPartitionValue("PENDING") .addSortValue("2025-11-04") .addSortValue(100))));

자세한 내용은 DynamoDB의 글로벌 보조 인덱스에 대한 다중 키 지원을 참조하세요.