Scan de DynamoDB en Python (boto3)
Un scan en boto3 son dos decisiones: la petición y cómo la paginas. El fragmento de abajo usa el paginador integrado, que no es un envoltorio que alguien haya escrito alrededor de tu bucle. Son cinco líneas de configuración de botocore, y esas cinco líneas deciden si tu scan es correcto y cuánto cuesta. (Si deberías estar haciendo scan siquiera es otra cuestión.)
Código
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("scan")
items = []
for page in paginator.paginate(
TableName="Music",
FilterExpression="#filter0 >= :filterValue0",
ExpressionAttributeNames={"#filter0": "Year"},
ExpressionAttributeValues={":filterValue0": {"N": "2010"}},
):
items.extend(page["Items"])
print(f"Matched {len(items)} items")Explicación
- El paginador es datos, no código. botocore incluye una entrada por operación en
paginators-1.json; la deScandice{"input_token": "ExclusiveStartKey", "output_token": "LastEvaluatedKey", "limit_key": "Limit", "result_key": ["Items", "Count", "ScannedCount"], "non_aggregate_keys": ["ConsumedCapacity"]}. Todo lo de abajo se desprende de esas claves. PaginationConfig={"PageSize": n}fijaLimit, porqueLimites lalimit_key.Limitacota los Items leídos, nunca los devueltos, así que con unaFilterExpressionuna página puede estar vacía y aun así tener coste.MaxItemscuenta los Items deresult_keyy devuelve unNextTokenque puedes pasar comoStartingTokenen un proceso posterior. No impide que la petición lea más allá de tu corte.build_full_result()agrega solo los campos deresult_key.Items,CountyScannedCountse suman;ConsumedCapacityes unanon_aggregate_key, así que el resultado combinado informa de la capacidad de una sola página como si fuera la del scan entero. Súmala tú, página a página, o subestimarás por un factor igual al número de páginas.- La
FilterExpressioncorre en el servidor después de la lectura, así que se te factura porScannedCount, no porCount.#filter0pone alias aYearporque es una palabra reservada; sin el alias la petición falla antes de leer nada. - Todos los errores llegan como
botocore.exceptions.ClientError. Ramifica segúne.response["Error"]["Code"]; las clases por error existen solo como atributos generados sobre el cliente (client.exceptions.ProvisionedThroughputExceededException), nunca como símbolos importables. - La API de recursos es la otra ergonomía.
Table.scanrecibe tipos nativos de Python, devuelve los números comodecimal.Decimaly construye filtros conAttr("Year").gte(2010)en vez de con mapas de marcadores.
Cuánto cuesta de verdad una página filtrada
60 Items de unos 2 KB cada uno, con Year = 2024 coincidiendo en dos de ellos, PageSize=10, ejecutado contra DynamoDB Local:
page 1: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 2: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 3: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 4: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 5: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 6: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 7: Count=0 ScannedCount=0 CU=0.0 LastEvaluatedKey=no
total CU across pages: 15.0Cuatro de las seis páginas reales no devolvieron nada, a precio completo. Esa es la forma del bug que el paginador existe para evitar: un bucle artesanal que corta cuando Items está vacío abandona en la página 1 e informa de cero cuando había dos canciones coincidentes.
La página 7 es la otra mitad. La página 6 alcanzó su Limit en el último Item de la tabla, así que DynamoDB devolvió un LastEvaluatedKey igualmente y el paginador gastó una ida y vuelta más para descubrir que no quedaba nada. Un LastEvaluatedKey significa «me detuve», no «hay más».
Llamar a build_full_result() sobre el mismo scan informa de CapacityUnits: 2.5. Las seis páginas consumieron 15,0.
Paginar sin escribir el bucle
El DynamoDB Query Builder ensambla el filtro, el mapa de alias y el bucle de paginación como un solo programa ejecutable, así que la trampa de Limit frente a Count de arriba queda resuelta antes de que lo pegues. Para paginar una tabla real de forma interactiva en vez de desde un script, descarga DynoTable.
Guías relacionadas
- Query vs. Scan — cuándo (rara vez) se justifica un
scan. - ¿Por qué mi Scan de DynamoDB es lento y caro? — el modelo de coste y cómo evitarlo.
- Scans paralelos — dividir la tabla con
Segment/TotalSegments, un paginador por segmento. - DynamoDB ProvisionedThroughputExceededException — lo que un scan de tabla completa le hace a la capacidad de una tabla aprovisionada.
Referencias
- Scan — Amazon DynamoDB API Reference
- scan — Boto3 DynamoDB.Client Reference
- Scan paginator — Boto3 DynamoDB Reference
- Scanning tables — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.