Profi6 Min. Lesezeit

Warum ein GSI in DynamoDB Schreibvorgänge auf der Basistabelle throttelt

Du schreibst in deine Tabelle. Der Schreibvorgang scheitert mit einer Durchsatz-Exception — aber die Exception nennt einen Global Secondary Index, nicht die Tabelle. Die Tabelle hat freie Kapazität.

Aus SQL kommend ist das Unsinn: Ein Sekundärindex kann kein INSERT blockieren. In DynamoDB kann er das, und der Mechanismus heißt GSI-Back-Pressure.

Warum throttelt ein DynamoDB-GSI Schreibvorgänge auf der Basistabelle?

DynamoDB throttelt den Schreibvorgang auf der Basistabelle, weil jeder Schreibvorgang auch in jeden GSI repliziert wird, und wenn eine GSI-Partition ihren Anteil nicht absorbieren kann, übt DynamoDB Back-Pressure aus, damit der Index nicht dauerhaft zurückfällt. Ein unterdimensionierter GSI-Key oder einer mit geringer Kardinalität wird so zu einer harten Obergrenze für deine Schreibrate auf der Basistabelle.

  • Ein Schreibvorgang auf der Basistabelle schreibt auch in jeden GSI. Wenn ein GSI seinen Anteil nicht absorbieren kann, throttelt DynamoDB den Schreibvorgang der Basistabelle, damit der Index nicht dauerhaft zurückfällt. (AWS-Doku)
  • Eine gleichmäßige Basistabelle rettet dich nicht. Der GSI wird nach seinem eigenen Key partitioniert. Ein GSI-Key mit geringer Kardinalität (wie status) erzeugt eine heiße Index-Partition, selbst wenn die Schreibvorgänge der Basistabelle perfekt verteilt sind.
  • Die Exception lügt über das Opfer. Die ResourceArn zeigt auf den GSI; die Operation, die tatsächlich gethrottelt wird, ist dein Schreibvorgang in die Tabelle.
  • Die Lösung ist Kapazität oder Key-Design, nicht Retry-Schleifen — erhöhe den GSI-Durchsatz oder wähle einen GSI-Partition-Key, der streut.

Wie ein einzelner Schreibvorgang den Index berührt

Ein PutItem auf der Basistabelle ist nicht ein Schreibvorgang. DynamoDB repliziert die projizierten Attribute des Items asynchron, nach einem letztendlich konsistenten Modell, in jeden GSI. Ein logischer Schreibvorgang fächert zu N physischen Schreibvorgängen auf — Tabelle plus jeder Index.

Diese Replikation ist nicht kostenlos und nicht optional. Der GSI muss mithalten, sonst driftet der Index bei jeder Operation weiter von der Tabelle weg.

Um diese Drift zu stoppen, wendet DynamoDB Back-Pressure an: Es throttelt den Quell-Schreibvorgang, damit der Index nie unbegrenzt veraltet.

Die Schreibkapazität des GSI ist also eine harte Obergrenze für die Schreibrate deiner Basistabelle — obwohl du nie direkt in den GSI schreibst.

Ein durchgespieltes Beispiel: eine Bestelltabelle

Angenommen, du betreibst eine Bestelltabelle. Das Basis-Item:

fieldvaluenote
PK"CUST#8841"partition key
SK"ORD#2026-06-23#A7"sort key
order_state"PROCESSING"
warehouse"EU-MAD-2"
total_cents4990

Die Schreibvorgänge der Basistabelle sind gesund. CUST#... hat hohe Kardinalität, also verteilen sich Bestellschreibvorgänge gleichmäßig über die Basispartitionen. Kein Hot Key, reichlich Kapazität.

Jetzt fügst du einen GSI hinzu, um „zeige mir jede Bestellung in einem bestimmten Status" zu beantworten:

GSI: orders-by-state
fieldvaluenote
GSI-PKorder_state"PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED"
GSI-SKSK

Vier mögliche Partition-Key-Werte. Während eines Flash-Sales landet fast jede neue Bestellung in order_state = "PENDING". Jeder dieser Schreibvorgänge trifft dieselbe GSI-Partition.

Diese Partition hat ein Durchsatzlimit pro Partition, und du hast gerade deinen gesamten Schreibsturm darauf gerichtet.

Die Basistabelle ist in Ordnung. Die PENDING-GSI-Partition brennt. DynamoDB throttelt das PutItem der Basistabelle, um den Index zu schützen.

Der Ablauf, der dich beißt

Hier ist der Back-Pressure-Pfad — Basis-Schreibvorgang ausgewogen, Index-Schreibvorgang konzentriert:

PutItemorder_state=PENDINGBasistabelleverteilt nach CUST#Async replizierenin GSIGSI-PartitionPENDING (heiß)PartitionslimitüberschrittenThrottle denBASIS-Schreibvorgang

Der Throttle wandert rückwärts: Eine heiße GSI-Partition weist den Schreibvorgang der Basistabelle ab, der sie gespeist hat.

Lies die Exception, nicht dein Bauchgefühl

Der Exception-Typ sagt dir genau, welche Obergrenze du getroffen hast. Die ResourceArn nennt den GSI; die gethrottelte Operation ist weiterhin der Tabellen-Schreibvorgang.

ModusReason-CodeWas ausging
ProvisionedIndexWriteProvisionedThroughputExceededProvisioned-Schreibkapazität des GSI
ProvisionedIndexWriteKeyRangeThroughputExceededEine einzelne heiße GSI-Partition
On-DemandIndexWriteMaxOnDemandThroughputExceededKonfigurierte On-Demand-Obergrenze des GSI
On-DemandIndexWriteAccountLimitExceededAccount-/Region-Durchsatzgrenze

Quelle: Understanding GSI write throttling and back pressure.

Der KeyRange-Grund ist der verräterische Hinweis auf den Hot-Partition-Fall oben: Die Gesamtkapazität des GSI kann in Ordnung aussehen, während ein Key-Bereich gesättigt ist.

Wie du es behebst

Gib dem GSI Raum. Die einfachste Ursache ist Unterdimensionierung. Ein GSI hat seine eigene Lese- und Schreibkapazität, vollständig getrennt von der Tabelle — siehe GSI vs. LSI.

Wenn du die Tabelle großzügig provisioniert und den GSI dünn gelassen hast, erhöhe die Schreibkapazität des GSI (oder sein On-Demand-Maximum).

Repariere den Partition Key. Kapazität rettet einen Key mit geringer Kardinalität nicht — du kannst eine einzelne heiße Partition nicht überprovisionieren. Wähle einen GSI-Partition-Key, der streut.

Setze ihn zusammen: order_state#shard, wobei shard ein kleines zufälliges Suffix ist, oder falte das Datum ein (PENDING#2026-06-23). Schreibvorgänge verteilen sich über Partitionen, und du fragst einen Status weiterhin per Query über die Shards ab.

Projiziere weniger Attribute. Jeder GSI-Schreibvorgang kopiert die projizierten Attribute. Eine KEYS_ONLY- oder enge INCLUDE-Projektion bedeutet kleinere Index-Schreibvorgänge und weniger Druck als ALL. Projiziere nicht, was du nie aus dem Index liest.

Verwirf den GSI, wenn er nur fürs Reporting ist. Wenn „Bestellungen nach Status" eine gelegentliche Admin-Frage ist und kein heißer Pfad, kann ein periodischer Scan mit einem Filter einen dauerhaft heißen Index schlagen — wäge es gegen Query vs. Scan ab.

Wenn du diesen Index abfragst, schreibt der Expression Builder die KeyConditionExpression für dich — z. B. #s = :state AND begins_with(SK, :prefix) — mit korrekt escapeten Namen und Werten:

KeyConditionExpression     "#s = :state"
ExpressionAttributeNames   { "#s": "order_state" }
ExpressionAttributeValues  { ":state": { "S": "PENDING" } }

Die Falle, die du dir merken solltest

Der relationale Instinkt — „Indizes verlangsamen Schreibvorgänge nur ein bisschen" — lässt sich nicht übertragen. Ein DynamoDB-GSI ist eine Durchsatz-Abhängigkeit, keine passive Struktur. Unterdimensioniere ihn oder wähle einen klumpenden Key, und er übt Back-Pressure auf die Tabelle aus, der er dient.

Beobachte die ConsumedWriteCapacityUnits und ThrottledRequests des GSI pro Partition, nicht nur die der Tabelle.

Nächste Schritte

  • GSI vs. LSI — warum ein GSI seine eigene Kapazität und einen anderen Partition Key hat.
  • Single-Table-Design — einen GSI überladen, um viele Muster zu bedienen, ohne heiße Indizes zu vervielfachen.
  • Query vs. Scan — wann ein Index seine Schreibkosten nicht wert ist.

Probiere DynoTable aus, um die verbrauchte Kapazität und Throttle-Events eines GSI gegen deine eigenen Tabellen zu beobachten, bevor ein Sale sie rot färbt.

Aktualisiert