Warum ein DynamoDB-GSI Base-Table-Writes drosselt
Du schreibst auf deine Tabelle. Der Write scheitert mit einer Throughput- Exception — aber die Exception nennt einen Global Secondary Index, nicht die Tabelle. Die Tabelle hat Spare Capacity.
Aus SQL ist das Unsinn: ein Secondary Index kann kein INSERT blockieren. In
DynamoDB kann er das, und der Mechanismus heißt GSI Back-Pressure.
Warum drosselt ein DynamoDB-GSI Base-Table-Writes?
DynamoDB drosselt den Base-Table-Write, weil jeder Write auch in jeden GSI repliziert — und wenn eine GSI-Partition ihren Anteil nicht absorbieren kann, wendet DynamoDB Back-Pressure an, damit der Index nicht dauerhaft zurückfällt. Ein unterprovisionierter oder low-cardinality GSI-Key wird so zur harten Decke deiner Base-Table-Write-Rate.
- Ein Write auf die Base Table schreibt auch in jeden GSI. Wenn ein GSI seinen Anteil nicht absorbieren kann, drosselt DynamoDB den Base-Table-Write, damit der Index nicht dauerhaft zurückfällt. (AWS docs)
- Eine gleichmäßige Base Table rettet dich nicht. Der GSI ist nach seinem
eigenen Key partitioniert. Ein low-cardinality GSI-Key (wie
status) erzeugt eine , selbst wenn Base-Table-Writes perfekt verteilt sind. - Die Exception lügt über das Opfer. Die
ResourceArnzeigt auf den GSI; die Operation, die wirklich gedrosselt wird, ist dein Write auf die Tabelle. - Der Fix ist Capacity oder Key-Design, nicht Retry-Loops — hebe GSI- Throughput an, oder wähle einen GSI-, der streut.
Wie ein einzelner Write den Index berührt
Ein PutItem auf der Base Table ist nicht ein Write. DynamoDB repliziert die
projezierten Attribute des Items asynchron in jeden GSI, auf einem eventually
consistent Modell. Ein logischer Write fächert zu N physischen Writes auf —
Tabelle plus jeder Index.
Diese Replikation ist nicht gratis und nicht optional. Der GSI muss mithalten, sonst driftet der Index bei jeder Operation weiter von der Tabelle weg.
Um diesen Drift zu stoppen, wendet DynamoDB Back-Pressure an: es drosselt den Source-Write, damit der Index nie ungebunden stale wird.
Die Write Capacity des GSI ist also eine harte Decke deiner Base-Table-Write- Rate — obwohl du nie direkt auf den GSI schreibst.
Praxisbeispiel: eine Orders-Tabelle
Angenommen, du betreibst eine Orders-Tabelle. Das Base-Item:
| 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 |
Base-Table-Writes sind gesund. CUST#... hat hohe Kardinalität, also streuen
Order-Writes gleichmäßig über Base-Partitionen. Kein Hot Key, reichlich Capacity.
Jetzt fügst du einen GSI hinzu, um „zeig mir jede Order in einem gegebenen State“ zu beantworten:
| field | value | note |
|---|---|---|
| GSI-PK | order_state | "PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED" |
| GSI-SK | SK |
Vier mögliche Partition-Key-Werte. Während eines Flash Sale landet fast jede
neue Order in order_state = "PENDING". Jeder dieser Writes trifft dieselbe
GSI-Partition.
Diese Partition hat ein Per-Partition-Throughput-Limit, und du hast gerade deinen gesamten Write-Storm darauf gerichtet.
Die Base Table ist fine. Die PENDING-GSI-Partition brennt. DynamoDB drosselt
das Base-Table-PutItem, um den Index zu schützen.
Der Flow, der dich beißt
Back-Pressure-Pfad — Base-Write balanciert, Index-Write konzentriert:
Eine heiße GSI-Partition lehnt den Base-Table-Write ab, der sie gefüttert hat.
Lies die Exception, nicht dein Bauchgefühl
Der Exception-Typ sagt dir genau, welche Decke du getroffen hast. Die
ResourceArn nennt den GSI; die gedrosselte Op ist trotzdem der Table-Write.
| Mode | Reason code | Was ausging |
|---|---|---|
| Provisioned | IndexWriteProvisionedThroughputExceeded | provisionierte Write Capacity des GSI |
| Both | IndexWriteKeyRangeThroughputExceeded | eine einzelne heiße GSI-Partition |
| On-demand | IndexWriteMaxOnDemandThroughputExceeded | konfigurierte Max-On-Demand-Decke des GSI |
| On-demand | IndexWriteAccountLimitExceeded | Account-/Region-Throughput-Grenze |
Quelle: Understanding GSI write throttling and back pressure.
Der KeyRange-Reason ist der Verräter für den Hot-Partition-Fall oben: die
Gesamt-GSI-Capacity kann fine aussehen, während eine Key-Range gesättigt ist.
So behebst du es
Gib dem GSI Raum. Die einfachste Ursache ist Under-Provisioning. Ein GSI hat seine eigene Read- und Write-Capacity, vollständig getrennt von der Tabelle — siehe GSI vs LSI.
Hast du die Tabelle großzügig provisioniert und den GSI dünn gelassen, hebe die Write Capacity des GSI an (oder sein On-Demand-Max).
Fix den Partition Key. Capacity rettet keinen low-cardinality Key — du kannst eine einzelne heiße Partition nicht herausprovisionieren. Wähle einen GSI- Partition Key, der streut.
Compose ihn: order_state#shard, wobei shard ein kleines Random-Suffix ist,
oder fold das Datum ein (PENDING#2026-06-23). Writes streuen über Partitionen
und du Queryst einen State weiterhin, indem du die Shards queryst.
Projeziere weniger Attribute. Jeder GSI-Write kopiert die projezierten
Attribute. Eine KEYS_ONLY- oder enge INCLUDE-Projektion heißt kleinere
Index-Writes und weniger Druck als ALL. Projeziere nicht, was du nie vom Index
liest.
Drop den GSI, wenn er nur für Reporting ist. Wenn „Orders by State“ eine gelegentliche Admin-Frage ist, kein Hot Path, kann ein periodischer Scan mit Filter einen permanent heißen Index schlagen — wäge es gegen Query vs Scan.
Wenn du diesen Index queryst, schreibt der
Expression Builder die
KeyConditionExpression für dich — z. B. #s = :state AND begins_with(SK, :prefix) —
mit korrekt escaped Names und Values:
KeyConditionExpression "#s = :state AND begins_with(SK, :prefix)"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" }, ":prefix": { "S": "ORD#2026-06-23" } }
Die Falle zum Merken
Der relationale Instinkt — „Indexes verlangsamen Writes nur ein bisschen“ — überträgt sich nicht. Ein DynamoDB-GSI ist eine Throughput-Dependency, keine passive Struktur. Unterdimensioniere ihn oder wähle einen klumpenden Key, und er übt Back-Pressure auf die Tabelle aus, die er bedient.
Beobachte ConsumedWriteCapacityUnits und WriteThrottleEvents auf der GSI-
Dimension, nicht nur der der Tabelle, und nutze Contributor Insights, um die
heißen Keys zu finden.
Nächste Schritte
- GSI vs LSI — warum ein GSI eigene Capacity und einen anderen Partition Key hat.
- Single-Table Design — einen GSI überladen, um viele Patterns zu bedienen, ohne heiße Indexes zu multiplizieren.
- Query vs Scan — wann ein Index seine Write-Kosten nicht wert ist.
Probier DynoTable, um jeden GSI auf deinen Tabellen zu inspizieren — Key Schema und Item Counts — und deine Indexes zu querien, bevor ein Sale sie rot färbt.