Fortgeschritten8 Min. Lesezeit

Wie du Daten in DynamoDB modellierst

In SQL modellierst du zuerst Entitäten und Beziehungen und vertraust dann darauf, dass der Query-Planner später zusammensetzt, was auch immer du verlangst. DynamoDB kehrt das um. Du modellierst die Lesevorgänge, von denen du bereits weißt, dass du sie machen wirst, und die Keys existieren, um sie zu bedienen.

Es gibt keine Join-Engine und keinen Planner, der zur Laufzeit eine Strategie wählt. Eine Query liest eine Partition entlang eines Keys, und das ist der gesamte Performance-Vertrag. Also designst du Keys für bekannte Zugriffsmuster, nicht für ein ordentliches Schema.

AWS sagt es unverblümt in seinem Best-Practices-Guide: „Du solltest nicht anfangen, dein Schema zu designen, bevor du die Fragen kennst, die es beantworten muss.“

Dieser Guide geht den ganzen Prozess an einer Domäne durch: einem Multiplayer-Game-Leaderboard, das Spieler, die Matches, die sie spielen, und ihr Ranking pro Saison verfolgt. Wir gehen von einer Liste von Fragen zu einem funktionierenden Key-Schema.

Wie modellierst du Daten in DynamoDB?

Modelliere zuerst die Lesevorgänge, nicht die Tabellen. Liste jede Query auf, die die App macht, dann designe einen und einen , sodass jede Frage sich zu einer einzelnen Query oder einem GetItem auflöst. Verorte Items, die zusammen gelesen werden, am selben Platz, ranghe über Werte im Sort Key, und füge einen GSI für jedes Zugriffsmuster hinzu, das die Basistabelle nicht bedienen kann.

  • Liste zuerst die Lesevorgänge auf, nicht die Tabellen. Die Fragen sind die Spezifikation; die Substantive sind Ablenkung.
  • Jede Frage muss eine Query oder ein GetItem sein. Wenn eine Frage einen Scan braucht, ist das Modell falsch.
  • Am selben Platz verortete Items teilen sich einen ; alles, worüber du rangst, kommt in den .
  • Eine Frage, die die Basistabelle nicht beantworten kann, bekommt einen — nie einen Scan mit einem Filter.

Schritt 1 — Fasse das Problem als Fragen, nicht als Tabellen

Widersteh dem Drang, players-, matches- und scores-Tabellen zu zeichnen. Dieser Instinkt ist die SQL-Gewohnheit, und hier ist er falsch. Schreib stattdessen jeden Lesevorgang auf, den die App tatsächlich ausführt. Für unser Leaderboard:

  • Das Profil eines Spielers per id abrufen.
  • Die jüngsten Matches eines Spielers auflisten, das Neueste zuerst.
  • Die Top-N-Spieler für eine gegebene Saison zeigen, nach Rating gerankt.
  • Einen Spieler über seinen öffentlichen Handle nachschlagen (z. B. für eine Profil-URL).

Diese vier Fragen — nicht die Substantive — sind die Spezifikation. Jede muss sich zu einer einzelnen Query (oder einem GetItem) auflösen, denn das ist die einzige Zugriffsform, die DynamoDB in großem Maßstab günstig bedient.

Wenn eine Frage nur durch das Scannen der Tabelle beantwortet werden kann, ist das Modell falsch, und du wirst es in Latenz und Kosten spüren — siehe Query vs. Scan dafür, warum ein Scan der Footgun ist, den man vermeiden sollte.

Die ganze Methode ist eine kurze, geordnete Pipeline, die du einmal pro Domäne durchläufst:

NeinJaJaNeinEntitäten auflistenZugriffsmuster aufzählenPK / SK entwerfen,die sie bedienenJeder Readeine Query?Pro übrigem Readeinen GSIGegen echteDaten prüfenNeue Fragetaucht auf?Modell ausliefern

Jeder Schritt unten bildet auf eine Box ab: auflisten, aufzählen, Keys designen, Indizes für den Rest hinzufügen, dann validieren.

Schritt 2 — Verstehe die Primitive, mit denen du modellierst

Eine Tabelle hat einen Partition Key (PK), der auswählt, auf welcher physischen Partition ein Item lebt, und einen optionalen Sort Key (SK), der Items innerhalb dieser Partition ordnet.

Die AWS-Core-Components-Docs nennen das Paar den Primary Key des Items. Eine Query zielt immer auf genau einen PK-Wert und kann den SK per Range-Scan durchgehen oder filtern — das ist das ganze Werkzeug.

Dieses Single-Partition-Design ist das, was DynamoDB die vorhersehbaren, latenzarmen, horizontal partitionierten Lesevorgänge liefern lässt, die erstmals im Amazon-Dynamo-Paper von 2007 beschrieben wurden.

Zwei Konsequenzen treiben jede Entscheidung unten:

  1. Items, die zusammen gelesen werden, sollten sich einen Partition Key teilen, sodass eine Query sie in einem einzelnen abgerechneten Request zurückgibt.
  2. Alles, worüber du rangen willst (jüngste Matches, Top-Ratings), muss im Sort Key leben, denn das ist das einzige Attribut, das eine Query ordnen und begrenzen kann.

Wenn eine Frage eine andere Zugriffsform braucht, als die Basistabelle bietet, fügst du einen Global Secondary Index hinzu — eine Re-Projektion der Tabelle unter einem anderen PK/SK.

(Zu GSI versus Local Secondary Index siehe GSI vs. LSI.)

Schritt 3 — Designe die Keys, eine Frage nach der anderen

Wir verwenden eine einzelne Tabelle mit generischen, überladenen Key-Attributen — den Single-Table-Ansatz — weil ein Spieler und seine Matches zusammen gelesen werden.

Erfinde deine eigenen Präfixe; hier taggen PLAYER#, MATCH# und SEASON# den Entitätstyp innerhalb ansonsten generischer Keys.

Fragen 1 und 2 (Profil + jüngste Matches) teilen sich eine Partition, sodass beide am selben PK hängen:

partitionIdrangeIdattributes
PLAYER#u8231PROFILEhandle, region, createdAt
PLAYER#u8231MATCH#2026-06-23T14result=win, ratingDelta=+18, mapId
PLAYER#u8231MATCH#2026-06-23T11result=loss, ratingDelta=-15, mapId

Query partitionId = "PLAYER#u8231" gibt das Profil und jedes Match in einem Lesevorgang zurück. Für das Profil allein GetItem.

Für jüngste Matches durchläuft rangeId begins_with "MATCH#" mit ScanIndexForward = false sie das Neueste zuerst — der Zeitstempel im Sort Key erledigt die Ordnung gratis.

Fragen 3 und 4 können aus dieser Partition nicht beantwortet werden — sie schwenken auf Saison-Rang und Handle, keiner davon ist der Basis-PK. Jede bekommt einen GSI.

Wir fügen zwei Paare generischer Index-Attribute hinzu — seasonPartition / seasonSort für den Rang-Index und handlePartition / handleSort für den Handle-Index — befüllt auf demselben Profil-Item (dem in Schritt 3 geschriebenen, jetzt mit seinen ausgefüllten Index-Attributen gezeigt):

partitionIdrangeIdseasonPartitionseasonSorthandlePartitionhandleSort
PLAYER#u8231PROFILESEASON#2026-Q2RATING#1842HANDLE#nighthawkPLAYER#u8231

Jetzt gibt eine Query auf den Saison-Index WHERE seasonPartition = "SEASON#2026-Q2" mit ScanIndexForward = false die Spieler nach Rating gerankt zurück — das ist das Leaderboard.

Ein zweiter Index, verschlüsselt auf handlePartition = "HANDLE#…", löst einen öffentlichen Handle in einem Lesevorgang zu einer Spieler-id auf. Eine physische Tabelle, vier Single-Query-Zugriffsmuster.

Eine -Anmerkung zu RATING#1842: DynamoDB sortiert Sort Keys lexikografisch, nicht numerisch, sodass ein Rating auf eine feste Breite zero-gepaddet werden muss (RATING#01842), sonst würde 9 nach 1000 sortieren. Das ist ein klassischer Modellierungs-Fallstrick, den man vorab richtig hinbekommen sollte.

Schritt 4 — Validiere das Modell in DynoTable

Ein Key-Schema verdient erst Vertrauen, wenn du beobachtest, wie eine echte Query genau die Items zurückgibt, die du erwartet hast, und nichts weiter.

Öffne die Tabelle in DynoTable, führe die Leaderboard-Query gegen den Saison-Index aus und bestätige, dass die Partition gerankt und begrenzt zurückkommt — kein Scan, kein clientseitiges Sortieren.

Die Saison-Leaderboard-Query gegen den GSI in DynoTable ausführen und das gerankte Ergebnis inspizieren.
Die Saison-Leaderboard-Query gegen den GSI in DynoTable ausführen und das gerankte Ergebnis inspizieren.

Wenn du die Condition-Expressions für diese Queries baust — das begins_with, das seasonPartition = :p, das Platzhalter-:p-Binding — lass das den DynamoDB Expression Builder machen.

Er generiert die KeyConditionExpression, die ExpressionAttributeNames und die ExpressionAttributeValues, sodass ein reserviertes Wort wie result oder ein vertippter Platzhalter nie stillschweigend einen Lesevorgang bricht.

Schritt 5 — Fallstricke und nächste Schritte

Ein paar Fallen, die man vor dem Ausliefern des Modells prüfen sollte:

  • Modelliere keine Beziehungen, die du nie zusammen liest. Ein GSI pro Frage ist günstig; ein verschwendeter GSI ist wiederkehrende Kosten. Füge Indizes aus der Frageliste hinzu, nicht spekulativ.
  • Beobachte die Partition-Hitze. Wenn ein PK (ein Prominenten-Spieler, eine einzelne Hot Season) den meisten Traffic absorbiert, kann diese Partition drosseln. Verteile Schreibvorgänge mit einem Suffix-Shard, wenn ein Key nachweislich heiß ist — AWS behandelt das unter Partition-Key-Design.
  • Zero-padde und ISO-8601-e alles Numerische oder Temporale in einem Sort Key, sodass die lexikografische Ordnung zu der Ordnung passt, die du meinst.
  • Eine neue Frage = ein neuer Key oder Index, nie ein Scan. Wenn später ein wirklich neues Zugriffsmuster auftaucht, erweitere die Keys; überdecke es nicht mit einem Filter.

Modelliere zuerst die Fragen, designe Keys, sodass jede eine Query ist, und beweise es dann.

Für einen Vorsprung beim mittleren Schritt: Das kostenlose Single-Table-Design-Tool verwandelt eine Zugriffsmuster-Liste wie diese in einen PK/SK/GSI-Plan, mit Beispiel-Items und Kosten-Hinweisen.

Probiere DynoTable, um deine Tabelle zu durchsuchen, diese Queries gegen die Basistabelle und die GSIs nebeneinander laufen zu lassen und zu beobachten, wie die Zugriffsmuster, die du designt hast, genau das zurückgeben, was du geplant hast. Und für die Frage, die du nicht modelliert hast, führt seine SQL Workbench echte JOINs, GROUP BY und Aggregationen client-seitig aus.

Aktualisiert

Probiere dieses Design interaktiv aus

Skizziere deine Entitäten und Zugriffsmuster im kostenlosen DynamoDB Single-Table Design Tool — es schlägt PK/SK-Schlüsselvorlagen vor, zeigt eine Vorschau der Item-Collections und zeigt, welche Muster einen GSI brauchen.

Single-Table Design Tool öffnen