Por qué un GSI de DynamoDB limita las escrituras de la tabla base
Escribes a tu tabla. La escritura falla con una excepción de throughput — pero la excepción nombra un Global Secondary Index, no la tabla. La tabla tiene capacidad de sobra.
Si vienes de SQL, eso es un sinsentido: un índice secundario no puede bloquear
un INSERT. En DynamoDB sí puede, y el mecanismo se llama back-pressure del
GSI.
¿Por qué un GSI de DynamoDB limita las escrituras de la tabla base?
DynamoDB limita la escritura de la tabla base porque cada escritura también se replica a cada GSI, y si una partición del GSI no puede absorber su parte, DynamoDB aplica back-pressure para que el índice no se quede permanentemente atrás. Así que un GSI infraaprovisionado o con una clave de baja cardinalidad se convierte en un techo duro sobre tu tasa de escritura de la tabla base.
- Una escritura a la tabla base también escribe a cada GSI. Si un GSI no puede absorber su parte, DynamoDB limita la escritura de la tabla base para que el índice no se quede permanentemente atrás. (AWS docs)
- Una tabla base equilibrada no te salva. El GSI se particiona por su propia
clave. Una clave de GSI de baja cardinalidad (como
status) crea una aunque las escrituras de la tabla base estén perfectamente repartidas. - La excepción miente sobre la víctima. El
ResourceArnapunta al GSI; la operación que de verdad se está limitando es tu escritura a la tabla. - El arreglo es capacidad o diseño de clave, no bucles de retry — sube el throughput del GSI, o elige una del GSI que se reparte.
Cómo una sola escritura toca el índice
Un PutItem en la tabla base no es una sola escritura. DynamoDB replica los
atributos proyectados del item a cada GSI de forma asíncrona, con un modelo de
consistencia eventual. Una escritura lógica hace fan-out a N escrituras físicas
— tabla más cada índice.
Esa replicación no es gratis ni opcional. El GSI tiene que seguir el ritmo, o el índice se aleja más de la tabla en cada operación.
Para parar ese drift, DynamoDB aplica back-pressure: limita la escritura fuente para que el índice nunca se quede stale sin cota.
Así que la capacidad de escritura del GSI es un techo duro sobre tu tasa de escritura de la tabla base — aunque nunca escribas al GSI directamente.
Ejemplo trabajado: una tabla de pedidos
Digamos que llevas una tabla de pedidos. El item base:
| field | value | note |
|---|---|---|
| PK | "CUST#8841" | partition key |
| SK | "ORD#2026-06-23#A7" | sort key |
| order_state | "PROCESSING" | |
| warehouse | "EU-MAD-2" | |
| total_cents | 4990 |
Las escrituras de la tabla base están sanas. CUST#... tiene alta cardinalidad,
así que las escrituras de pedidos se reparte de forma uniforme entre las
particiones base. Sin clave caliente, capacidad de sobra.
Ahora añades un GSI para responder «muéstrame cada pedido en un estado dado»:
| field | value | note |
|---|---|---|
| GSI-PK | order_state | "PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED" |
| GSI-SK | SK |
Cuatro valores posibles de clave de partición. Durante un flash sale, casi cada
pedido nuevo aterriza en order_state = "PENDING". Cada una de esas escrituras
golpea la misma partición del GSI.
Esa partición tiene un límite de throughput por partición, y acabas de apuntar toda tu tormenta de escrituras a ella.
La tabla base está bien. La partición PENDING del GSI está en llamas.
DynamoDB limita el PutItem de la tabla base para proteger el índice.
El flujo que te muerde
Path de back-pressure — escritura base equilibrada, escritura de índice concentrada:
Una partición caliente del GSI rechaza la escritura de la tabla base que la alimentó.
Lee la excepción, no tu intuición
El tipo de excepción te dice exactamente qué techo has golpeado. El
ResourceArn nombra el GSI; la op limitada sigue siendo la escritura a la
tabla.
| Modo | Reason code | Qué se agotó |
|---|---|---|
| Provisioned | IndexWriteProvisionedThroughputExceeded | Capacidad de escritura provisionada del GSI |
| Ambos | IndexWriteKeyRangeThroughputExceeded | Una sola partición caliente del GSI |
| On-demand | IndexWriteMaxOnDemandThroughputExceeded | Techo max on-demand configurado del GSI |
| On-demand | IndexWriteAccountLimitExceeded | Límite de throughput de cuenta/región |
Fuente: Understanding GSI write throttling and back pressure.
El reason KeyRange es la pista del caso de partición caliente de arriba: la
capacidad global del GSI puede verse bien mientras un rango de clave está
saturado.
Cómo solucionarlo
Dale aire al GSI. La causa más simple es el infraaprovisionamiento. Un GSI tiene su propia capacidad de lectura y escritura, totalmente separada de la tabla — ver GSI vs LSI.
Si provisionaste la tabla con generosidad y dejaste el GSI delgado, sube la capacidad de escritura del GSI (o su max on-demand).
Arregla la clave de partición. La capacidad no salva una clave de baja cardinalidad — no puedes out-provisionar una sola partición caliente. Elige una clave de partición del GSI que se reparte.
Compónla: order_state#shard donde shard es un sufijo aleatorio pequeño, o
mete la fecha (PENDING#2026-06-23). Las escrituras se reparte entre
particiones y sigues haciendo Query de un estado consultando los shards.
Proyecta menos atributos. Cada escritura al GSI copia los atributos
proyectados. Una proyección KEYS_ONLY o un INCLUDE ceñido significa
escrituras de índice más pequeñas y menos presión que ALL. No proyectes lo
que nunca leerás del índice.
Tira el GSI si solo es para reporting. Si «pedidos por estado» es una pregunta ocasional de admin, no un hot path, un scan periódico con filtro puede ganar a un índice permanentemente caliente — pésalo contra Query vs Scan.
Cuando hagas Query a ese índice, el
Expression Builder escribe el
KeyConditionExpression por ti — p. ej. #s = :state AND begins_with(SK, :prefix) —
con los nombres y valores escapados correctamente:
KeyConditionExpression "#s = :state AND begins_with(SK, :prefix)"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" }, ":prefix": { "S": "ORD#2026-06-23" } }
La trampa a recordar
El instinto relacional — «los índices solo ralentizan un poco las escrituras» — no se transfiere. Un GSI de DynamoDB es una dependencia de throughput, no una estructura pasiva. Infradimensiónalo o elige una clave que se agrupa, y hace back-pressure a la tabla a la que sirve.
Mira ConsumedWriteCapacityUnits y WriteThrottleEvents en la dimensión del
GSI, no solo en la de la tabla, y usa Contributor Insights para encontrar las
claves calientes.
Próximos pasos
- GSI vs LSI — por qué un GSI tiene su propia capacidad y una clave de partición distinta.
- Single-table design — sobrecargar un solo GSI para servir muchos patrones sin multiplicar índices calientes.
- Query vs Scan — cuándo un índice no vale su coste de escritura.
Prueba DynoTable para inspeccionar cada GSI de tus tablas — key schema y conteos de items — y hacer Query a tus índices antes de que una sale los ponga en rojo.