在 Node.js(AWS SDK v3)中查詢 DynamoDB GSI
GSI 查詢就是一次普通的 Query 加上 IndexName,然後有兩件事不再照表格查詢的方式運作:你習慣的那個一致性旗標會變成錯誤,而分頁游標會多長出一個屬性。這裡 AlbumTitle-index 依專輯取歌,而基礎表格(Artist + SongTitle)若不掃描就做不到這件事。
程式碼
import {DynamoDBClient, QueryCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const items = [];
let lastEvaluatedKey;
do {
const response = await client.send(
new QueryCommand({
TableName: 'Music',
IndexName: 'AlbumTitle-index',
KeyConditionExpression: '#hashKey = :hashKeyValue',
ExpressionAttributeNames: {
'#hashKey': 'AlbumTitle'
},
ExpressionAttributeValues: {
':hashKeyValue': {S: 'Danzon'}
},
ExclusiveStartKey: lastEvaluatedKey
})
);
items.push(...(response.Items ?? []));
lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);
console.log(`Found ${items.length} songs on the album`);游標有三個屬性寬,不是兩個
拿上面那個迴圈去跑同一張專輯上的 300 首歌,然後檢查它交還的 LastEvaluatedKey:
table query -> ['Artist', 'SongTitle']
GSI query -> ['AlbumTitle', 'Artist', 'SongTitle']GSI 的鍵不是唯一的,所以光靠索引鍵無法續讀一次掃描。DynamoDB 會把索引鍵和基礎表格的鍵一起回傳,兩者都必須原封不動地放回 ExclusiveStartKey。這就是為什麼一個自己刻的、只存「我看到的最後一個 sort key」的游標在表格上能運作,到了索引上卻會悄悄漏掉或重複項目 — 也是為什麼當表格的鍵是一個你不希望外洩的使用者 id 時,把那個鍵持久化到用戶端是個壞主意。
ConsistentRead: true 是 400,不是升級
直覺會告訴你強一致讀取花更多容量,換來更新鮮的資料。在 GSI 上,它花掉的是你的整個請求:
ValidationException: Consistent reads are not supported on global secondary indexes
HTTP 400API 參考同樣直白:"Strongly consistent reads are not supported on global secondary indexes. If you query a global secondary index with ConsistentRead set to true, you will receive a ValidationException." 對 GSI 執行 Scan 也會以同樣的訊息拒絕同一個旗標。local secondary index 則接受它。
已於 2026-07-28 在 node v24.18.0 上,以 @aws-sdk/client-dynamodb 3.1095.0 對照 DynamoDB Local(amazon/dynamodb-local)重現。錯誤文字與鍵的形狀皆為引擎自身的輸出。
說明
IndexName不會取代TableName。兩者放在同一個指令裡,接著KeyConditionExpression指名的是索引的 partition key(AlbumTitle),不是表格的,運算子集合則與表格查詢相同。- 你拿到的只有投影出來的東西。GSI 查詢回傳的是索引所投影的內容(
ALL、KEYS_ONLY,或INCLUDE清單),而依 API 參考的說法:"global secondary index queries cannot fetch attributes from the parent table"。少了某個屬性,就代表要對基礎鍵補一次GetItem,或是加寬投影並重建索引。 - 缺少索引鍵的項目永遠不會出現。那就是稀疏索引模式,而且是個功能:只把
status = "OPEN"的資料列納入索引,GSI 就能維持小巧。這也是為什麼 GSI 查詢可能回傳比你預期更少的項目,卻不會拋出任何錯誤。 - 複寫是非同步的,所以剛落地到表格的一次寫入,可能還不在索引裡。請在「寫後讀」的路徑上把這件事納入考量,而不是用緊密迴圈一直重試。
改用視覺化操作
事後才補上一個 GSI,是學會這件事最昂貴的方式。單一表格設計規劃器會拿你的存取模式,推算出其中哪些需要索引鍵、哪些基礎表格本來就服務得了。
若想瀏覽一張表格的各個索引,並從表單執行 GSI 查詢、搭配分頁格線,請下載 DynoTable。
相關範例
- Python 中查詢 DynamoDB GSI — 以 boto3 做同一次索引查詢。
- 使用 AWS CLI 查詢 DynamoDB GSI — 從 shell 做同一次索引查詢。
- Node.js 中的 DynamoDB Query — 查詢基礎表格。
- GSI 與 LSI 的比較 — 哪一種索引型別適合這個存取模式。
- 為什麼 GSI 是最終一致的 — 複寫延遲的說明。
- "The table does not have the specified index" — 索引名稱對不上(GSI 名稱區分大小寫)。
- "Consistent reads are not supported on global secondary indexes" — 為什麼強一致讀取旗標在 GSI 上會失敗。