Fortgeschritten4 Min. Lesezeit

Single-Table-Design in DynamoDB

Aus SQL kommend ist der Instinkt eine Tabelle pro Entität: customers, orders, order_items. In DynamoDB ist dieser Instinkt meist falsch. Eine einzelne Tabelle, die jede Entität speichert, unterschieden durch überladene Schlüsselpräfixe, lässt dich einen Elter und seine Kinder in einem Query holen — keine Joins, kein N+1.

Von den Zugriffsmustern ausgehen, nicht von den Entitäten

Single-Table-Design ist zugriffsmuster-zuerst. Bevor du einen einzelnen Schlüssel wählst, schreibe jeden Lesevorgang auf, den deine App macht — „Profil eines Kunden holen“, „Bestellungen eines Kunden, neueste zuerst, auflisten“, „alle offenen Bestellungen finden“ — denn die Schlüssel existieren nur, um diese Liste zu bedienen. Relationale Normalisierung optimiert Speicher; DynamoDB-Modellierung optimiert die Abfragen, von denen du bereits weißt, dass du sie ausführen wirst. Zähle sie auf, und gestalte dann Schlüssel so, dass jede ein einzelnes Query ist.

Die Idee

Wähle generische Schlüsselnamen (PK, SK) und kodiere den Entitätstyp in den Wert:

PKSKattributes
CUSTOMER#42PROFILEname, email, plan
CUSTOMER#42ORDER#2026-001total, status
CUSTOMER#42ORDER#2026-002total, status

Jetzt liefert ein Query PK = "CUSTOMER#42" das Profil und jede Bestellung in einem berechneten Lesevorgang. SK begins_with "ORDER#" verengt es auf nur die Bestellungen.

Visuell stapeln sich die überladenen Items unter einem Partition Key als eine einzige Item-Collection:

Partition: CUSTOMER#42SK: PROFILESK: ORDER#2026-001SK: ORDER#2026-002One Query

Ein Lesevorgang der Partition gibt den Kunden und jede Bestellung zusammen zurück.

Überladene GSIs

Derselbe Trick funktioniert auf Indizes. Setze einen generischen GSI1PK/GSI1SK auf Items, und ein einzelner GSI bedient mehrere Zugriffsmuster, je nachdem, was jedes Item in diese Attribute schreibt:

PKSKGSI1PKGSI1SK
ORDER#001METADATASTATUS#OPEN2026-01-04
ORDER#002METADATASTATUS#OPEN2026-01-05

Jetzt listet Query GSI1 WHERE GSI1PK = "STATUS#OPEN" offene Bestellungen nach Datum — ein Muster, das die Basistabelle nicht beantworten kann. Eine andere Entität kann GSI1 mit eigener Bedeutung wiederverwenden (z. B. CATEGORY#books). Ein Index, viele Abfragen.

Many-to-Many: die Adjazenzliste

Für Beziehungen (ein Benutzer in vielen Teams, ein Team mit vielen Benutzern) schreibe die Kante zweimal mit getauschten IDs: PK=USER#1, SK=TEAM#9 und PK=TEAM#9, SK=USER#1. Eine der Seiten abzufragen listet die andere — der DynamoDB-Ersatz für eine Join-Tabelle.

Wann nicht Single-Table

Es ist nicht gratis. Eine überladene Tabelle ist schwerer zu durchdenken, schwerer weiterzuentwickeln und analytikfeindlich. Wenn deine Zugriffsmuster wirklich unbekannt sind oder sich ständig ändern, oder die Daten überwiegend analytisch sind, können separate Tabellen (oder ein anderer Store) die vernünftigere Wahl sein. Single-Table gewinnt, wenn die Muster bekannt und hochvolumig sind.

Kosten der falschen Form

Als separate Tabellen zu modellieren erzwingt einen Scan oder clientseitigen Join, um einen Kunden wieder zusammenzusetzen, und das ist die Scan-Fußangel. Modelliere die Zugriffsmuster zuerst, und gestalte dann Schlüssel so, dass jedes ein Query ist.

Schätze, was diese Items pro Lesevorgang kosten, mit dem Item-Größen- & Kapazitätsrechner, und probiere DynoTable, um ein Single-Table-Schema zu durchsuchen und die überladenen Collections nebeneinander zu sehen.

Aktualisiert