Cómo se guarda internamente un GSI de DynamoDB
Un no es un puntero de vuelta a tu tabla. Es una tabla aparte, gestionada internamente — sus propias particiones, su propio key schema, su propia capacidad — que DynamoDB mantiene en sync copiando escrituras en ella de forma asíncrona.
Si vienes de SQL, un índice es un B-tree atornillado a la misma tabla física, actualizado dentro de la misma transacción. Un GSI rompe ambas asunciones, y casi cada sorpresa de GSI se remonta a ese único hecho.
¿Cómo se guarda un GSI de DynamoDB?
Un GSI de DynamoDB se guarda como una tabla aparte, gestionada internamente — sus propias particiones, key schema y capacidad — no como un puntero a la tabla base. DynamoDB copia cada escritura al índice de forma asíncrona, guardando solo las claves del GSI, las claves de la tabla base y cualquier atributo .
- Un GSI es su propia tabla. Tiene un espacio de particiones totalmente independiente claveado por la clave de partición del GSI, no la de la tabla base.
- Las escrituras se replican de forma asíncrona. Tu escritura hace commit primero a la tabla base; luego DynamoDB la reparte a cada GSI en un path de background.
- Solo se guardan los atributos proyectados. El índice guarda las claves del GSI, las claves base, más los atributos que proyectaste — nada más.
- La clave del GSI no tiene que ser única. Varios items base pueden compartir una clave de partición/sort del GSI; la base es el desempate que los mantiene distintos.
Empieza con un item base
Toma un audit log SaaS. Cada acción privilegiada en un workspace se
convierte en un evento inmutable. La tabla base, WorkspaceEvents, está
claveada para que todos los eventos de un workspace vivan en una sola
, ordenados por tiempo:
| EventPK | EventSK | actorId | verb | targetRef |
|---|---|---|---|---|
| WS#orbit-9 | TS#2026-06-23T14:02:11Z | USR#kp | ROLE_GRANTED | USR#mara |
EventPK = "WS#orbit-9" particiona por workspace; EventSK es un timestamp
ISO para que un Query devuelva los eventos de un workspace en orden
cronológico. Eso sirve «muéstrame el timeline de este workspace» a la
perfección.
No sirve nada más. No puedes preguntar «¿qué hizo USR#kp a lo largo de cada
workspace?» — actorId no es una clave, así que la única forma de responderlo
en la tabla base es un Scan completo. Ese es
el access pattern que un GSI existe para añadir.
Añade un GSI y mira aparecer una segunda tabla
Define un GSI, ByActor, que re-particiona los mismos eventos por quién los
hizo:
ByActor (GSI)
partition key = actorId ("USR#kp")
sort key = EventSK ("TS#2026-06-23T14:02:11Z")
DynamoDB ahora mantiene una segunda estructura física. El mismo evento lógico
se guarda dos veces — una en la partición WS#orbit-9 de la tabla base, y
otra en la partición USR#kp del GSI:
| actorId | EventSK | EventPK | verb |
|---|---|---|---|
| USR#kp | TS#2026-06-23T14:02:11Z | WS#orbit-9 | ROLE_GRANTED |
Nota lo que viajó junto: las claves de la tabla base (EventPK,
EventSK) se guardan en cada item del GSI automáticamente. Así es cómo un hit
de GSI puede apuntarte de vuelta al item completo — y por qué un índice
KEYS_ONLY sigue costando storage.
Qué vive de verdad en el GSI
El índice no copia el item entero. Cada entrada del GSI guarda exactamente tres cosas, y solo controlas la tercera:
| Guardado en el GSI | De dónde viene | ¿Opcional? |
|---|---|---|
| Clave de partición + sort del GSI | Los atributos que nombraste como claves del GSI | No |
| Clave(s) de la tabla base | Copiada de cada item base | No |
| Atributos proyectados | Tu elección de Projection | Sí |
Projection es KEYS_ONLY, INCLUDE (una lista nombrada) o ALL. Un Query
sobre el GSI solo puede devolver atributos que están en el índice.
Pide uno que no esté proyectado y DynamoDB no lo trae de forma transparente — no obtienes nada de vuelta para ese campo. (documentación de GSI de AWS)
Esa es la trampa relacional al revés: SQL haría join de vuelta al heap por la columna que falta. Un GSI nunca lo hace. La es todo el contrato.
Cómo una escritura llega al índice
La replicación es la parte que rompe más fuerte la intuición SQL. Una escritura base y su update del índice no son una sola operación atómica.
Cuando haces PutItem, DynamoDB hace commit durable a la tabla base, te
reconoce la escritura y luego propaga el cambio por un path de background
que actualiza cada GSI. El acknowledgment no espera al índice.
Orden de eventos para nuestra escritura de audit, de arriba abajo:
El caller obtiene su 200 OK en el paso tres, antes de que terminen los pasos
cuatro a seis — así que un Query sobre ByActor en el hueco puede perderse
un evento brand-new.
Esa asincronía es por diseño. Viene del linaje del paper de Amazon Dynamo de 2007, que eligió disponibilidad sobre consistencia síncrona. Las consecuencias completas viven en por qué un GSI es de consistencia eventual.
La clave del GSI no es una clave única
En SQL, un índice secundario no-único es el default y uno único es una constraint a la que optas. Un GSI es lo contrario: no tiene garantía de uniqueness, nunca.
Dos eventos de audit del mismo actor en timestamps que colisionan compartirían
el mismo GSI1PK y GSI1SK. DynamoDB guarda ambos — los desambigua
internamente por la primary key de la tabla base, que siempre viaja junto.
Así que un Query al GSI por un actor en un instante puede devolver
legítimamente varios items. Si asumiste una-fila-por-clave como te daría un
índice único SQL, ese es el pie.
Cuando haces Query al índice, el
DynamoDB Expression Builder escribe el
KeyConditionExpression con names y values escapados correctamente — p. ej.
matcheando un actor desde un cutoff:
KeyConditionExpression: "#a = :actor AND #ts > :since"
ExpressionAttributeNames: { "#a": "actorId", "#ts": "EventSK" }
ExpressionAttributeValues: {
":actor": { "S": "USR#kp" },
":since": { "S": "TS#2026-06-01T00:00:00Z" }
}La capacidad vive con el índice, no con la tabla
Como el GSI es su propia tabla, tiene su propia capacidad de lectura y
escritura, facturada y limitada por separado de la tabla base. Una lectura de
ByActor consume las unidades de lectura del GSI, nunca las de la tabla.
El acoplamiento inverso es la parte que muerde. Cada escritura a la tabla base también escribe el índice, y si el GSI no puede absorber eso, hace back-pressure a la escritura base. Ese mecanismo tiene su propia guía — cuando un GSI limita las escrituras de la tabla base.
También por eso la clave de partición de un GSI importa tanto como la de la tabla base. Una clave de GSI de baja cardinalidad agrupa escrituras en una sola partición del índice aunque las escrituras base estén perfectamente repartidas — una partición caliente que creaste al re-clavear.
Amplificación de escritura del GSI (facturada)
Cada escritura a la tabla base que se proyecta a un GSI cuesta WCU base +
WCU del índice en on-demand de us-east-1. Un item de 1 KB con
proyección ALL suele facturar ~2 WCU en total — uno por la fila de la
tabla, uno por la copia del índice. KEYS_ONLY encoge la escritura del índice;
ALL duplica storage y write amp. Modela el tamaño del item y la proyección
en la calculadora de precios.
Escollos y próximos pasos
- No esperes de vuelta atributos no proyectados. Un
Queryal GSI solo devuelve lo que el índice guarda. Si necesitas el item completo, proyéctalo o tráelo de la tabla base por las claves que viajan junto. - No trates una clave de GSI como única. Planifica que un
Querydevuelva más de un item por clave; la primary key base es la única identidad real. - No leas un GSI justo después de la escritura que lo alimentó. El path async significa que el índice puede no mostrar aún tu escritura — lee la tabla base cuando necesitas read-your-own-writes.
- Dimensiona la capacidad del GSI a propósito. Es independiente en lecturas y una dependencia oculta en escrituras.
Todo el juego es elegir formas de clave que sirvan tus patrones — el single-table design sobrecarga un solo GSI a lo largo de muchos de ellos; GSI vs LSI cubre cuándo encaja un índice local en su lugar.
Construye y previsualiza tu KeyConditionExpression del GSI en el
DynamoDB Expression Builder, luego
prueba DynoTable para inspeccionar los atributos proyectados de
un índice y ver cómo las escrituras se replican al GSI en tus propias tablas.