DynamoDB 中基於分區鍵和排序鍵的查詢
1. 簡介
Amazon DynamoDB是 AWS 提供的核心服務之一。它廣泛用於建立快速、可擴展且無伺服器的應用程式。它提供了完全託管的 NoSQL 資料庫解決方案,在任何規模下都具有個位數毫秒的效能。與傳統的關聯式資料庫不同,DynamoDB 使用鍵值和文件資料模型,鼓勵預先設計資料存取模式。
在本教程中,我們將重點介紹 DynamoDB 最強大的功能之一:使用複合主鍵查詢數據,它結合了分區鍵和排序鍵。我們將介紹此複合鍵模型的工作原理,並示範如何使用 AWS SDK for Java 有效地查詢資料。
2. 理解複合鍵模型
DynamoDB 支援兩種類型的主鍵:簡單鍵(僅限分區鍵)和複合鍵,它結合了分區鍵和排序鍵。
分區鍵決定資料的儲存位置,而排序鍵允許在該分區內進行排序和過濾。此模型將相關資料(如使用者訂單)放在一個鍵下。例如,在UserOrders
表中:
-
userId
可以是 HashKey -
orderDate
可以是 Range Key
透過此設定可以輕鬆檢索使用者的所有訂單、按日期排序或按時間範圍篩選,所有這些都只需一個查詢即可完成。
3. Maven依賴
為了從 Java 應用程式與 DynamoDB 交互,我們將使用AWS SDK for Java v2 ,它提供了一個現代的非阻塞 API:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
<version>2.31.26</version>
</dependency>
這使我們可以存取DynamoDbClient
和其他必要的類別來對我們的表格執行查詢。
4.按分區鍵查詢
在 DynamoDB 中查詢資料的最簡單、最常見的方式是使用分區鍵。當我們透過分區鍵查詢時,DynamoDB 會傳回所有共享相同分區鍵值的項目。
假設我們的UserOrders
表儲存了多個使用者的訂單,並且我們想要檢索單一使用者所下達的所有訂單。由於userId
是我們的分區鍵,我們可以使用基本查詢來執行此操作:
QueryRequest queryRequest = QueryRequest.builder()
.tableName("UserOrders")
.keyConditionExpression("userId = :uid")
.expressionAttributeValues(Map.of(
":uid", AttributeValue.builder().s("user1").build()
)).build();
QueryResponse response = dynamoDbClient.query(queryRequest);
在上面的範例中,我們使用DynamoDbClient
來執行查詢。我們使用建構器模式來建立請求,從而可以輕鬆建立複雜的請求。執行查詢後,結果將在QueryResponse
物件中傳回。要存取實際數據,我們使用items()
方法:
List<Map<String, AttributeValue>> items = response.items();
for (Map<String, AttributeValue> item : items) {
System.out.println("Order item: " + item.get("item").s());
}
此清單包含每個項目作為屬性名稱到值的映射,您可以進一步處理或將其轉換為特定於應用程式的物件。
5. 使用分區鍵和排序鍵進行查詢
雖然僅透過分區鍵查詢可能有用,但有時我們需要更精確的過濾。我們可以透過將分區鍵與排序鍵的條件結合來實現這一點。這使我們能夠過濾分區內的結果,例如按日期範圍或特定前綴。
假設我們想要檢索 user1 在 2025 年 1 月 1 日之後下的所有訂單。由於orderDate
是我們的排序鍵,我們可以在keyConditionExpression
中包含一個比較:
QueryRequest queryRequest = QueryRequest.builder()
.tableName("UserOrders")
.keyConditionExpression("userId = :uid AND orderDate > :startDate")
.expressionAttributeValues(Map.of(
":uid", AttributeValue.builder().s("user1").build(),
":startDate", AttributeValue.builder().s("2025-01-01").build()
)).build();
QueryResponse response = dynamoDbClient.query(queryRequest);
在此查詢中,我們使用分區鍵和排序鍵的條件來縮小結果範圍。 DynamoDB 將僅掃描user1
的分區並傳回orderDate
在 2025-01-01 之後的項目。這種方法很有效,因為它避免了掃描不相關的資料。
6. 常見範圍關鍵條件
DynamoDB 支援多個有用的運算符,用於按排序鍵進行篩選。這些允許我們使用標準比較邏輯在分區內微調我們的查詢。
6.1.之間
我們可以使用BETWEEN
來檢索特定範圍內的項目。這在處理時間戳或日期時特別有用:
QueryRequest queryRequest = QueryRequest.builder()
.tableName("UserOrders")
.keyConditionExpression("userId = :uid AND orderDate BETWEEN :from AND :to")
.expressionAttributeValues(Map.of(
":uid", AttributeValue.builder().s("user1").build(),
":from", AttributeValue.builder().s("2024-12-01").build(),
":to", AttributeValue.builder().s("2024-12-31").build()
)).build();
這將返回用戶 1 在 2024 年 12 月下達的所有訂單。
6.2. BEGINS_WITH
如果我們的排序鍵是字串(如格式化的日期),我們可以查詢以特定前綴開頭的所有項目。這對於按年份、月份或任何基於字串的前綴進行分組很有幫助。
QueryRequest queryRequest = QueryRequest.builder()
.tableName("UserOrders")
.keyConditionExpression("userId = :uid AND begins_with(orderDate, :prefix)")
.expressionAttributeValues(Map.of(
":uid", AttributeValue.builder().s("user1").build(),
":prefix", AttributeValue.builder().s("2025-01").build()
)).build();
這將返回 2025 年 1 月下的所有訂單。
7. 處理查詢中的分頁
DynamoDB 將每個查詢回應的大小限制為 1 MB 資料。如果我們的查詢符合的結果不只這些,DynamoDB 會在回應中傳回一個LastEvaluatedKey
,我們可以使用它繼續取得下一頁的結果。
為了解決這個問題,我們應該循環將結果分頁:
List<Map<String, AttributeValue>> allItems = new ArrayList<>();
Map<String, AttributeValue> lastKey = null;
do {
QueryRequest.Builder requestBuilder = QueryRequest.builder()
.tableName("UserOrders")
.keyConditionExpression("userId = :uid")
.expressionAttributeValues(Map.of(
":uid", AttributeValue.fromS(userId)
));
if (lastKey != null) {
requestBuilder.exclusiveStartKey(lastKey);
}
QueryResponse response = dynamoDb.query(requestBuilder.build());
allItems.addAll(response.items());
lastKey = response.lastEvaluatedKey();
} while (lastKey != null && !lastKey.isEmpty());
return allItems;
<此模式確保我們檢索所有符合的項目,無論每頁返回多少個。在查詢大分區或進行報告樣式匯出時它很有用。
8. 結論
DynamoDB 的複合鍵模型為我們提供了一種高效組織和檢索資料的強大方法。透過使用分區鍵和範圍鍵,我們可以組織相關資料並為高效能應用程式建立高效、可擴展的存取模式。
在本文中,我們探討如何僅使用分區鍵來查詢表,以及如何透過包含範圍鍵來過濾分區內的結果來增強這些查詢。
與往常一樣,程式碼範例可在 GitHub 上找到。