DynamoDB Scan in Go (AWS SDK v2)
NewScanPaginator è un cursore, non una collection: parte ottimista, finisce esaurito, e prende la dimensione della pagina da un posto che lo ScanInput che hai davanti non mostra. Per capire quando evitare del tutto Scan, vedi Query vs Scan.
Codice
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func main() {
ctx := context.TODO()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
if err != nil {
log.Fatalf("load config: %v", err)
}
client := dynamodb.NewFromConfig(cfg)
paginator := dynamodb.NewScanPaginator(client, &dynamodb.ScanInput{
TableName: aws.String("Music"),
FilterExpression: aws.String("#filter0 >= :filterValue0"),
ExpressionAttributeNames: map[string]string{
"#filter0": "Year",
},
ExpressionAttributeValues: map[string]types.AttributeValue{
":filterValue0": &types.AttributeValueMemberN{Value: "2010"},
},
})
var items []map[string]types.AttributeValue
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
log.Fatalf("scan: %v", err)
}
items = append(items, page.Items...)
}
fmt.Printf("Matched %d items\n", len(items))
}Cosa restituisce davvero HasMorePages()
Su una fixture di 600 canzoni di circa 3,9 KB per Item, di cui 8 soddisfano Year >= 2010 e stanno in fondo all'ordinamento:
fresh HasMorePages(): true (before any request)
page 1: len(page.Items)=0 ScannedCount=271 CU=128.5 LastEvaluatedKey set
page 2: len(page.Items)=0 ScannedCount=271 CU=128.5 LastEvaluatedKey set
page 3: len(page.Items)=8 ScannedCount= 58 CU= 27.5 LastEvaluatedKey nil
exhausted HasMorePages(): false
NextPage() after exhaustion: err = "no more pages available"HasMorePages() è true prima della prima chiamata, quindi il loop parte sempre almeno una volta. Non puoi usarlo per verificare se una tabella è vuota. Poi due pagine hanno restituito zero Item mentre lo scan era ancora in corso, il che significa che if len(page.Items) == 0 { break } riporta un risultato vuoto su una tabella che contiene 8 corrispondenze. E il paginator è monouso: dopo il loop il valore è esaurito, e NextPage restituisce un errore invece di ripartire. Scansionare di nuovo significa chiamare di nuovo NewScanPaginator.
Limit vive in due posti, e vince l'opzione
Go divide la dimensione della pagina tra la struct di input e le opzioni del paginator. Impostali entrambi e ha la precedenza l'opzione:
ScanInput.Limit = 500, ScanPaginatorOptions.Limit = 25 -> ScannedCount 25Assegnare in.Limit = aws.Int32(500) e aspettarsi pagine da 500 Item è una svista silenziosa quando una functional option da qualche altra parte ha già impostato 25. Se tocchi solo ScanInput, il paginator lo rispetta.
Misurato il 2026-07-28 su DynamoDB Local (amazon/dynamodb-local) con aws-sdk-go-v2/service/dynamodb v1.62.1 su go1.26.5.
Spiegazione
page.Itemsè[]map[string]types.AttributeValue, non la tua struct.attributevalue.UnmarshalListOfMaps(items, &songs)converte il blocco in una sola chiamata e trasformaYearin unintGo; i tag di structdynamodbavcontrollano il mapping.- I numeri sono stringhe sul filo. Il valore grezzo è
&types.AttributeValueMemberN{Value: "2010"}— il membroNcontiene unastringGo, quindistrconvsta su entrambi i lati di qualsiasi aritmetica se non fai l'unmarshal in una struct tipizzata. FilterExpressiongira dopo la lettura, ed è per questo che le due pagine vuote costano comunque 128,5 unità ciascuna. Il riferimento dell'API è esplicito: il filtro "does not consume any additional read capacity units", e il corollario è che non ne risparmia nemmeno.#filter0è obbligatorio, non stilistico.Yearè una parola riservata di DynamoDB; senza alias restituisceValidationException: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year.Segment/TotalSegmentsdanno a ogni goroutine la propria fetta della tabella, e ognuna ha bisogno del proprio paginator. Il parallelismo riduce il tempo reale e spende la stessa capacità.
Fallo visivamente
Il query builder DynamoDB emette la forma dell'intero programma — filtro, mappe di nomi e valori, e il loop del paginator — così le parti che questa pagina misura sono generate invece che ricordate.
Per vedere quanti Item tocca davvero un filtro prima di affidare lo scan a un servizio, scarica DynoTable ed eseguilo sulla tabella in una griglia.
Esempi correlati
- DynamoDB Scan in Java — lo stesso scan con AWS SDK for Java 2.x.
- DynamoDB Query in Go — la lettura più economica a cui dovresti di solito ricorrere.
- Query vs Scan — quando (raramente) uno
Scanè giustificato. - Perché il mio Scan DynamoDB è lento e costoso? — il modello di costo e come evitarlo.
- DynamoDB ProvisionedThroughputExceededException — cosa fa uno scan dell'intera tabella alla capacità di una tabella con provisioning.
- DynamoDB ThrottlingException — l'altro throttle, e come lo gestisce il backoff esponenziale.