Profi7 Min. Lesezeit

Wie ein DynamoDB-GSI intern gespeichert wird

Ein ist kein Pointer zurück in deine Tabelle. Es ist eine eigene, intern verwaltete Tabelle — eigene Partitionen, eigenes Key-Schema, eigene Capacity —, die DynamoDB synchron hält, indem es Writes asynchron hinein kopiert.

Kommst du von SQL, ist ein Index ein B-Tree an dieselbe physische Tabelle, aktualisiert in derselben Transaktion. Ein GSI bricht beide Annahmen — und fast jede GSI-Überraschung führt auf genau diese Tatsache zurück.

Wie wird ein DynamoDB-GSI gespeichert?

Ein DynamoDB-GSI wird als eigene, intern verwaltete Tabelle gespeichert — eigene Partitionen, Key-Schema und Capacity —, nicht als Pointer in die Basistabelle. DynamoDB kopiert jeden Write asynchron in den Index und speichert nur die GSI-Keys, die Basistabellen-Keys und Attribute.

  • Ein GSI ist seine eigene Tabelle. Er hat einen vollständig unabhängigen Partition-Space, keyed über den GSI-Partition-Key, nicht den der Basistabelle.
  • Writes replizieren asynchron. Dein Write commitet zuerst in die Basistabelle, dann fächert DynamoDB ihn auf einem Background-Pfad zu jedem GSI.
  • Nur projizierte Attribute werden gespeichert. Der Index hält die GSI-Keys, die Base-Keys plus die Attribute, die du projiziert hast — nichts anderes.
  • Der GSI-Key muss nicht unique sein. Mehrere Base-Items können sich einen GSI-Partition-/Sort-Key teilen; der Base- ist der Tiebreaker, der sie unterscheidbar hält.

Mit einem Base-Item starten

Nimm ein SaaS-Audit-Log. Jede privilegierte Aktion in einem Workspace wird ein unveränderliches Event. Die Basistabelle WorkspaceEvents ist so gekeyed, dass alle Events eines Workspace in einer leben, zeitlich geordnet:

WorkspaceEvents (base table)
EventPKEventSKactorIdverbtargetRef
WS#orbit-9TS#2026-06-23T14:02:11ZUSR#kpROLE_GRANTEDUSR#mara

EventPK = "WS#orbit-9" partitioniert nach Workspace; EventSK ist ein ISO- Timestamp, sodass eine Query die Events eines Workspace chronologisch liefert. Das bedient „zeig mir die Timeline dieses Workspace“ perfekt.

Alles andere nicht. Du kannst nicht fragen „was hat USR#kp über jeden Workspace getan?“ — actorId ist kein Key, der einzige Weg auf der Basistabelle ist ein voller Scan. Genau dafür existiert ein GSI.

GSI anlegen und zusehen, wie eine zweite Tabelle erscheint

Definiere einen GSI ByActor, der dieselben Events nach Ausführendem umpartitioniert:

ByActor (GSI)
partition key = actorId   ("USR#kp")
sort key      = EventSK   ("TS#2026-06-23T14:02:11Z")

DynamoDB hält jetzt eine zweite physische Struktur. Dasselbe logische Event liegt zweimal — einmal in der Basistabellen-Partition WS#orbit-9, nochmal in der GSI-Partition USR#kp:

ByActor (GSI) — its own partition space
actorIdEventSKEventPKverb
USR#kpTS#2026-06-23T14:02:11ZWS#orbit-9ROLE_GRANTED

Was mitkommt: die Keys der Basistabelle (EventPK, EventSK) liegen in jedem GSI-Item automatisch. So kann ein GSI-Hit zurück aufs volle Item zeigen — und deshalb kostet ein KEYS_ONLY- Index trotzdem Storage.

Was wirklich im GSI liegt

Der Index kopiert nicht das ganze Item. Jeder GSI-Eintrag hält genau drei Dinge, und du kontrollierst nur das dritte:

Im GSI gespeichertWoher es kommtOptional?
GSI-Partition + Sort KeyDie Attribute, die du als GSI-Keys nanntestNein
Basistabellen-Key(s)Von jedem Base-Item kopiertNein
Projizierte AttributeDeine Projection-WahlJa

Projection ist KEYS_ONLY, INCLUDE (benannte Liste) oder ALL. Eine Query auf dem GSI kann nur Attribute zurückgeben, die im Index sind.

Fragst du eines an, das nicht projiziert ist, holt DynamoDB es nicht transparent nach — für dieses Feld kommt nichts zurück. (AWS GSI docs)

Das ist die relationale Falle umgekehrt: SQL würde für die fehlende Spalte zurück auf den Heap joinen. Ein GSI tut das nie. Die ist der ganze Vertrag.

Wie ein Write den Index erreicht

Die Replikation bricht SQL-Intuition am härtesten. Ein Base-Write und sein Index-Update sind nicht eine atomare Operation.

Bei PutItem commitet DynamoDB dauerhaft in die Basistabelle, bestätigt deinen Write und propagiert dann die Änderung auf einem Background-Pfad, der jeden GSI aktualisiert. Die Bestätigung wartet nicht auf den Index.

Ablauf für unseren Audit-Write, von oben nach unten:

PutItemWS#orbit-9 eventCommit inBasispartition200 OKan CallerAsync-Pfad:GSI-Keys extrahierenRoute zu ByActorPartition USR#kpProjizierteAttribute schreiben

Der Caller bekommt sein 200 OK bei Schritt drei, bevor Schritte vier bis sechs fertig sind — eine Query auf ByActor in der Lücke kann ein brandneues Event verpassen.

Diese Asynchronität ist Absicht. Sie kommt aus der Linie des Amazon Dynamo Paper von 2007, das Availability über synchrone Consistency stellte. Die vollen Konsequenzen stehen in warum ein GSI eventually consistent ist.

Der GSI-Key ist kein Unique Key

In SQL ist ein nicht-unique Secondary Index der Default und ein unique einer ein Constraint, den du wählst. Ein GSI ist das Gegenteil: er hat keine Uniqueness-Garantie, jemals.

Zwei Audit-Events desselben Actors mit kollidierenden Timestamps würden sich dieselbe GSI1PK und GSI1SK teilen. DynamoDB speichert beide — es unterscheidet sie intern über den Primary Key der Basistabelle, der immer mitgeführt wird.

Eine GSI-Query für einen Actor zu einem Instant kann also legitim mehrere Items zurückgeben. Hast du One-Row-per-Key wie bei einem SQL Unique Index angenommen, ist das die Fußangel.

Wenn du den Index queryst, schreibt der DynamoDB Expression Builder die KeyConditionExpression mit korrekt escaped Names und Values — z. B. ein Actor seit einem 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" }
}

Capacity lebt beim Index, nicht bei der Tabelle

Weil der GSI seine eigene Tabelle ist, hat er eigene Read- und Write- Capacity, separat von der Basistabelle berechnet und gedrosselt. Ein Read von ByActor verbraucht die Read Units des GSI, nie die der Tabelle.

Die umgekehrte Kopplung beißt. Jeder Basistabellen-Write schreibt auch den Index, und wenn der GSI das nicht absorbieren kann, back-pressured er den Base-Write. Dieser Mechanismus hat einen eigenen Guide — wenn ein GSI Basistabellen-Writes drosselt.

Deshalb zählt der Partition Key eines GSI genauso wie der der Basistabelle. Ein GSI-Key mit niedriger Kardinalität klumpt Writes auf eine Index-Partition, selbst wenn Base-Writes perfekt verteilt sind — eine Hot Partition, die du durch Re-Keying erzeugt hast.

GSI Write Amplification (berechnet)

Jeder Basistabellen-Write, der in einen GSI projiziert, kostet Base WCU + Index WCU On-Demand in us-east-1. Ein 1‑KB-Item mit ALL-Projection berechnet typisch ~2 WCU total — eines für die Tabellenzeile, eines für die Index-Kopie. KEYS_ONLY schrumpft den Index-Write; ALL verdoppelt Storage und Write Amp. Item-Größe und Projection im Pricing Calculator modellieren.

Fallstricke und nächste Schritte

  • Erwarte keine nicht-projizierten Attribute zurück. Eine GSI-Query liefert nur, was der Index speichert. Brauchst du das volle Item: projizieren oder per mitgeführten Keys von der Basistabelle holen.
  • Behandle einen GSI-Key nicht als unique. Plane, dass eine Query mehr als ein Item pro Key zurückgibt; der Base Primary Key ist die einzige echte Identität.
  • Lies einen GSI nicht direkt nach dem Write, der ihn gefüttert hat. Der Async-Pfad heißt: der Index zeigt deinen Write vielleicht noch nicht — lies die Basistabelle, wenn du Read-your-own-writes brauchst.
  • Dimensioniere die Capacity des GSI bewusst. Unabhängig bei Reads, versteckte Abhängigkeit bei Writes.

Das ganze Spiel ist, Key-Shapes zu wählen, die deine Patterns bedienen — Single-Table Design überlädt einen GSI über viele hinweg; GSI vs LSI deckt ab, wann ein lokaler Index passt.

Baue und preview deine GSI-KeyConditionExpression im DynamoDB Expression Builder, dann DynoTable ausprobieren, um die projizierten Attribute eines Index zu inspizieren und Writes in den GSI auf eigenen Tabellen zu beobachten.

Aktualisiert