Por qué un Scan de DynamoDB es lento y caro
Un Scan lee cada elemento de la tabla y solo filtra después. Es la operación a la que
recurres por memoria muscular de SQL, y la que silenciosamente dispara tu factura mientras
hace tu latencia peor que la del servidor RDS que dejaste atrás.
¿Por qué mi Scan de DynamoDB es lento y caro?
Un Scan lee todos los elementos de la tabla antes de que se ejecute la FilterExpression, por
lo que pagas por leer toda la tabla sin importar cuántos resultados devuelva, y se vuelve más
lento a medida que la tabla crece. El arreglo casi siempre es una Query indexada — modela el
patrón de acceso alrededor de una clave para que DynamoDB toque una sola partición en lugar de
todo.
- Un
Scanlee toda la tabla, cada vez. El tamaño, no tu número de resultados, decide lo que pagas y cuánto tarda. - La
FilterExpressiones una mentira sobre el coste. Se ejecuta después de que la lectura está medida, así que devolver 12 elementos puede facturar la lectura de 12 millones. - Un
Scanse vuelve más lento a medida que creces. UnaQueryindexada se mantiene plana — toca una partición sin importar cuánto crezca la tabla. - El arreglo casi siempre es modelado, no ajuste. Si haces
Scanpara responder una pregunta rutinaria, te falta una clave.
Qué hace realmente un Scan
Viniendo de SQL, SELECT * FROM events WHERE type = 'checkout' parece gratis — el motor
tiene un índice, o no lo tiene, pero de cualquier forma obtienes filas de vuelta. En
DynamoDB no hay ningún planificador de consultas decidiéndolo por ti.
Un Scan recorre toda la tabla secuencialmente, 1 MB a la vez, y entrega cada página a tu
FilterExpression. Lo que el filtro rechaza aún se lee, aún se mide y aún está en tu
factura. (AWS: Scanning tables)
Esa es la trampa. El filtro parece una cláusula WHERE, pero cambia el conjunto de
resultados, nunca el coste. Un Scan consume la misma capacidad de lectura haya o no un
filtro presente. (AWS: Scanning tables)
Cuenta las unidades de lectura
DynamoDB mide las lecturas en unidades de capacidad de lectura (RCU). Una RCU compra una sola lectura fuertemente consistente de un elemento de hasta 4 KB; las lecturas eventualmente consistentes cuestan la mitad. Los elementos más grandes redondean hacia arriba al siguiente bloque de 4 KB. (AWS: Read/write capacity mode)
Toma una tabla de analítica, ProductEvents. Cada fila es un evento rastreado:
PK = "TENANT#acme"
SK = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytesDigamos que contiene 2.000.000 de eventos, cada uno de ~1 KB, todos bajo un inquilino muy activo. Quieres los checkouts de hoy. El movimiento reflejo:
Scan ProductEvents
FilterExpression: eventType = "checkout"
Ese filtro podría devolver 40 filas. Pero el Scan leyó los 2.000.000 de elementos
primero. A ~1 KB cada uno (1 RCU por 4 KB, eventualmente consistente ≈ 0,5 RCU por 4 KB),
mediste aproximadamente 250.000 RCU — y paginaste por ~500 MB de datos — para devolver
40 elementos.
Ahora modela el patrón de acceso como una clave y haz Query en su lugar:
Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"
Esto lee solo la porción coincidente de una partición. Si esas 40 filas de checkout más los demás eventos del día suman ~2 MB, pagas por ~2 MB de lecturas, no 500 MB. La misma respuesta, una fracción diminuta del coste — y la latencia se mantiene plana a medida que la tabla crece.
Scan vs Query, medidos
| Scan + filtro | Query indexada | |
|---|---|---|
| Lee | Cada elemento de la tabla | Una partición, acotada por SK |
| Capacidad facturada | Toda la tabla, antes del filtro | Solo los elementos de tu porción |
| Nuestro ejemplo | ~250.000 RCU (~500 MB) | unos pocos cientos de RCU (~2 MB) |
| Latencia | Crece con el tamaño de la tabla | Plana a medida que la tabla crece |
| Número de resultados | No decide nada sobre el coste | Coincide con lo que pagas |
La lección que codifica la tabla: en un Scan, tu número de resultados y tu factura no
están relacionados. En una Query, van a la par.
Decide antes de hacer Scan
La mayoría de los Scan accidentales vienen de una pregunta: ¿puedo nombrar la partición
que necesito? Si sí, es una Query. Si no, el arreglo es una clave, no un filtro más
grande.
Aquí está la decisión en forma de flujo.
El camino casi siempre termina en Query; solo caes hasta Scan cuando ninguna clave —
presente o añadible — encaja con el patrón de acceso.
Si el patrón es real y recurrente pero la tabla base no puede indexarlo, esa es la señal
para añadir un índice secundario global para que la pregunta se
convierta en una Query. Modelar tus claves en torno a tus patrones de acceso por
adelantado es todo el juego — consulta diseño de tabla única.
Escribe la consulta indexada, no un filtro
Cuando sí necesitas una condición más allá de la clave, constrúyela deliberadamente en
lugar de volcar todo en una FilterExpression. El
Constructor de expresiones de DynamoDB genera por ti
la KeyConditionExpression y los marcadores de atributo, para que la partition key y la
sort key hagan la acotación — antes de que DynamoDB mida la lectura, no después.
KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)
Cuándo un Scan está realmente bien
Un Scan no está prohibido — solo es el valor por defecto equivocado. Es la herramienta
correcta cuando genuinamente quieres decir "leer todo":
- Exportaciones puntuales o rellenos ejecutados a mano.
- Tablas diminutas de configuración / búsqueda donde toda la tabla son unos pocos KB.
- Trabajos en segundo plano que paginan toda la tabla a propósito. Divídelos entre
workers con
Segment/TotalSegments— un scan paralelo — en lugar de un largo rastreo secuencial. (AWS: Scanning tables)
Y ten en cuenta que PartiQL no te salva: SELECT * FROM ProductEvents WHERE eventType = 'checkout' sin un predicado de clave compila directamente a un Scan. Es el mismo tiro al
pie con ropa de SQL. (Consulta Query vs Scan para el desglose
completo.)
Cuando realmente necesitas analítica entre elementos — un GROUP BY, un JOIN, un agregado
que DynamoDB no puede expresar — el Workbench SQL de DynoTable los ejecuta del lado del
cliente sobre un conjunto de resultados acotado, en lugar de machacar la tabla con un Scan
completo.
Próximos pasos
Estima lo que cuesta cualquiera de los dos patrones con la calculadora de capacidad, lee Query vs Scan para el contraste a nivel de API, y descarga DynoTable para ejecutarlos contra tus propias tablas y ver tú mismo la capacidad consumida.