Query vs Scan en DynamoDB
Query lee una única colección de Items por clave de partición (acotando
opcionalmente por la clave de ordenación); Scan lee la tabla entera y filtra
después. Se parecen en la API, pero facturan —y escalan— de forma completamente
diferente.
¿Cuándo debo usar Query vs Scan en DynamoDB?
Usa Query siempre que puedas identificar la partición que necesitas — lee una única colección de Items y factura solo por los Items coincidentes. Recurre a Scan únicamente para exportaciones puntuales o tablas pequeñas; lee cada Item y factura la tabla entera antes de que se ejecute cualquier FilterExpression. Con datos reales, Query siempre gana.
- Query es dirigido: pagas por los Items de la partición coincidente.
- Scan es exhaustivo: pagas por leer cada Item y luego descartas la mayoría con
un
FilterExpressionque se ejecuta después de medir la lectura.
En una tabla de cualquier tamaño real, un Scan con filtro es el clásico footgun
del estilo «por qué mi factura es enorme y mi latencia es peor que la de RDS».
En paralelo
| Query | Scan | |
|---|---|---|
| Lecturas | Una partición (por PK) | Cada Item de la tabla |
| Capacidad facturada | Items coincidentes en la partición | Tabla entera, antes de filtrar |
FilterExpression | Aplicado tras la lectura —aún se factura la lectura | Igual —filtrar nunca reduce el coste |
| Latencia | Plana a medida que crece la tabla | Crece con el tamaño de la tabla |
| Paginación | 1 MB/página → LastEvaluatedKey | 1 MB/página; paralelizable |
| Úsalo para | Patrones de acceso conocidos | Exportaciones puntuales, tablas de config diminutas |
La trampa clave: un FilterExpression se ejecuta después de que DynamoDB mide
la lectura, en ambas operaciones. Un Scan que «devuelve 10 filas» puede facturar
por leer un millón —filtrar es una comodidad, nunca un control de coste.
Lo que cuesta realmente un Scan completo
Ponle números. DynamoDB contabiliza las lecturas en unidades de 4 KB: una lectura
cuesta una
por cada 4 KB, y una lectura
la mitad. Query y Scan
suman el tamaño de cada Item que tocan —no de cada Item que devuelven— y
redondean hacia arriba al siguiente múltiplo de 4 KB.
Toma una tabla de 1 millón de Items con una media de 2 KB por Item (~2 GB de datos) y un patrón de acceso que necesita 10 de esos Items:
| Items leídos | Datos contabilizados | Unidades de lectura (eventualmente consistente) | |
|---|---|---|---|
Scan + FilterExpression | 1,000,000 | ~2 GB | ~262,000 |
Query sobre una clave coincidente | 10 | 20 KB | 3 |
Los mismos 10 Items, a cinco órdenes de magnitud de distancia —y el Scan factura
esas ~262,000 cada vez que se ejecuta, tanto si el filtro coincide con diez Items
como si no coincide con ninguno. Con facturación son
unidades de solicitud que van directas a la factura; en tablas
, un Scan grande compite con el tráfico de
producción por el rendimiento y puede throttlearlo hasta un
ProvisionedThroughputExceededException.
Tres datos de coste más que sorprenden a la gente:
Select: COUNTno es gratis. Un Query o Scan de recuento consume exactamente la misma capacidad de lectura que leer los Items —simplemente no los devuelve.Limitlimita los Items evaluados, no los Items coincidentes. Combinado con un filtro, una página puede volver vacía y aun así facturar una página completa de lecturas.- Nunca tienes que adivinar. Pasa
ReturnConsumedCapacity: TOTALy cada respuesta informa de la capacidad que acaba de consumir.
Comprueba cuánto pesan tus propios Items con la calculadora de tamaño de Items y convierte después las unidades de lectura en una factura mensual con la calculadora de precios.
Usa Query
Query PK = "USER#42" AND SK begins_with "ORDER#"
Si te ves recurriendo a Scan para responder a un patrón de acceso común, eso es
una señal de modelado: añade un Global Secondary Index para
que el patrón se convierta en un Query.
La elección se reduce a una pregunta: ¿puedes nombrar la partición que necesitas?
Si conoces la clave, haces un Query; si no, añade un GSI para convertirlo en uno,
y recurre a Scan solo cuando ninguna clave encaje.
Cuándo está bien usar Scan
Exportaciones puntuales, tablas de configuración diminutas y trabajos en segundo
plano que paginan deliberadamente por toda la tabla. Usa
Segment/TotalSegments para repartir un Scan entre workers (un
—consulta
scans paralelos en DynamoDB) cuando de verdad
debas leerlo todo, y pagínalo correctamente con LastEvaluatedKey
(guía de paginación). Si el problema es un Scan que ya
ejecutas, por qué Scan es lento y caro
te guía por el triaje.
Un SELECT * FROM table reflejo sobre DynamoDB es el mismo antipatrón con ropa de
PartiQL —se compila a un Scan. Cuando de verdad necesitas analítica entre Items
(un GROUP BY, un JOIN, un agregado), el SQL Workbench de DynoTable los ejecuta
en el lado del cliente sobre un conjunto de resultados acotado en lugar de
machacar la tabla.
Prueba DynoTable para ejecutar e inspeccionar estas consultas contra tus propias tablas —muestra la capacidad consumida de cada operación que ejecuta.