Projection expressions de DynamoDB
Una projection expression es el SELECT col1, col2 de DynamoDB: una lista de nombres
de atributo separada por comas que le dice a GetItem, Query o Scan que devuelva solo
esos atributos en lugar del elemento completo.
¿Las projection expressions de DynamoDB reducen el coste de lectura?
No. Una ProjectionExpression recorta el payload de la respuesta, no la capacidad de lectura que se te factura. DynamoDB lee el elemento completo desde el almacenamiento, mide la sobre su tamaño en disco, y luego descarta los atributos que no nombraste a la salida. Para reducir realmente el coste de lectura, usa un de cobertura en su lugar.
- Recorta el payload, no el coste de lectura. DynamoDB lee (y factura) el elemento
completo desde el almacenamiento, luego descarta los atributos que no nombraste a la
salida.
ProjectionExpressiones una optimización de red, no de capacidad. - Es como traes un subconjunto público. Nombra los pocos atributos que un llamante tiene permitido ver; el resto nunca sale de la tabla.
- Usa marcadores
#namepara cualquier cosa que pudiera estar reservada. Los nombres de atributo planos en la expresión chocan con las ~570 palabras reservadas de DynamoDB y hacen fallar la petición. - Para un ahorro de lectura real, usa un índice de cobertura en su lugar. Un GSI que proyecta solo las columnas que necesitas se lee a su propio tamaño (más pequeño).
Qué ahorra realmente
Viniendo de SQL, asumirías que SELECT a, b escanea menos que SELECT *. En DynamoDB esa
intuición está mal. La unidad de capacidad de una
lectura se calcula a partir del tamaño del elemento en disco, redondeado hacia arriba
al siguiente bloque de 4 KB — antes de aplicar la proyección. AWS es explícito: una
ProjectionExpression no cambia la capacidad de lectura que consume una petición.1
Así que una proyección te ahorra dos cosas, ambas reales pero ambas posteriores a la lectura:
- Bytes por la red. Un elemento de 6 KB devuelto como dos atributos pequeños es una
respuesta diminuta. En una
Queryque devuelve cientos de elementos, eso suma rápido. - Trabajo del lado del cliente. Menos que deserializar, menos que mantener en memoria, menos que filtrar por accidente hacia un log o una respuesta de API.
Lo que no ahorra es la RCU. Ese es el tiro al pie: la gente recurre a una proyección para recortar su factura, no ve ningún cambio y concluye que DynamoDB está roto. No lo está — mediste la palanca equivocada.
Proyecta un perfil de usuario público
Digamos que gestionas un directorio de usuarios. Cada perfil es un elemento, indexado para que puedas traer a una persona por su nombre:
PK = "PROFILE#ada" (partition key)
SK = "PROFILE#ada" (sort key — single-item collection)
El elemento es gordo. Lleva la cara pública de la cuenta más un montón de atributos privados y operativos:
{
"PK": "PROFILE#ada",
"SK": "PROFILE#ada",
"displayName": "Ada L.",
"avatarUrl": "https://cdn.example.com/u/ada.png",
"bio": "Builds things.",
"emailAddress": "ada@example.com",
"passwordResetToken": "…",
"billingCustomerId": "cus_…",
"lastLoginIp": "…",
"internalRiskScore": 0.02
}Una tarjeta de perfil pública necesita tres campos. Traer el elemento entero significa que
emailAddress, lastLoginIp e internalRiskScore viajan a un contexto que nunca debería
verlos. Nombra solo el subconjunto público:
GetItem PK = "PROFILE#ada" SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio
La respuesta lleva tres atributos. Los privados se quedan en la tabla — no filtrados por tu app tras la llegada, sino nunca serializados en la respuesta en absoluto. Esa es la victoria de seguridad, y es la que cuesta deshacer una vez que un secreto ya ha cruzado un límite.
Puedes ensamblar y copiar esta petición exacta — nombres, marcadores y la llamada al SDK —
en el Constructor de expresiones de DynamoDB, que
emite por ti la ProjectionExpression y el mapa ExpressionAttributeNames.
Escapa las palabras reservadas con marcadores #
Aquí es donde una proyección limpia estalla. DynamoDB reserva una larga lista de palabras —
name, status, comment, size, timestamp y cientos más.2 Si un atributo
que estás proyectando es una de ellas, el nombre en bruto en la expresión se rechaza.
Supón que el perfil también tiene un atributo status ("active", "suspended"). Esto
falla:
ProjectionExpression displayName, status
status está reservado. El arreglo es un expression attribute name — un marcador con
prefijo # mapeado al nombre real:
ProjectionExpression displayName, #s
ExpressionAttributeNames { "#s": "status" }
El mismo mecanismo alcanza atributos anidados. Para sacar un único campo de un map, o un elemento de una list, usa la sintaxis de ruta de documento — y pon un marcador en cada segmento, ya que cualquiera de ellos podría estar reservado:
ProjectionExpression #addr.#city, tags[0]
ExpressionAttributeNames { "#addr": "address", "#city": "city" }
Una regla práctica: pon marcadores en todo. Nunca tienes que recordar cuál de las ~570 palabras reservadas estás pisando, y la expresión se lee igual de cualquier manera.
Cuándo un índice de cobertura gana a una proyección
Si genuinamente necesitas recortar el coste de lectura — no solo el payload — la palanca es
un índice secundario global que proyecta solo los atributos que lees. Un GSI es una
copia separada de los datos; eliges KEYS_ONLY, INCLUDE o ALL para su
proyección.3 Un índice KEYS_ONLY o un INCLUDE estrecho es físicamente más
pequeño por elemento, así que una Query contra él se mide a ese tamaño más pequeño.
Eso es un índice de cobertura: la consulta se responde enteramente desde el índice, sin viaje de vuelta a la tabla base. Úsalo cuando un patrón de lectura caliente solo necesita unos pocos atributos de elementos grandes.
ProjectionExpression | GSI de cobertura | |
|---|---|---|
| Recorta payload | Sí | Sí |
| Recorta coste de lectura | No | Sí — leído al tamaño del índice |
| Almacenamiento extra | Ninguno | Una segunda copia de los campos proyectados |
| Coste de escritura extra | Ninguno | Las escrituras se propagan al índice |
| Mejor para | Ocultar campos privados; pequeñas victorias | Lecturas calientes de unos pocos campos de elementos grandes |
El compromiso es honesto: el índice te cuesta almacenamiento y capacidad de escritura para
ahorrar capacidad de lectura. Vale la pena para una lectura frecuente de una porción fina de
un elemento pesado; no vale la pena para recortar un GetItem puntual. Consulta
GSI vs LSI para elegir el tipo de índice, y
cuándo una lectura de GSI puede estar obsoleta
antes de ponerlo en la ruta caliente.
Trampas y próximos pasos
- No esperes una factura más pequeña. Una proyección por sí sola nunca cambia la RCU. Si el número no se movió, ese es el comportamiento documentado, no un bug.
- Pon marcadores en las palabras reservadas. Un
nameostatuspelado en la expresión hace fallar la petición — mapéalo con#. - Proyectar atributos de clave es gratis y a menudo útil — DynamoDB los devuelve barato, y te permiten paginar o volver a traer.
- Recurre a un índice de cobertura solo cuando un patrón caliente lee unos pocos campos de elementos grandes; sopesa primero el coste de escritura/almacenamiento.
Construye la ProjectionExpression y su mapa de nombres-de-atributo en el
Constructor de expresiones, y
prueba DynoTable para ejecutar estas proyecciones contra tus propias tablas y
ver encogerse la respuesta.
- AWS DynamoDB Developer Guide, Working with Read and Write Operations — la capacidad de lectura se basa en el tamaño del elemento antes de aplicar cualquier
ProjectionExpression. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ↩ - AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html ↩
- AWS DynamoDB Developer Guide, Attribute Projections (
KEYS_ONLY/INCLUDE/ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html ↩