Einsteiger5 Min. Lesezeit

DynamoDB Projection Expressions

Eine Projection Expression ist das SELECT col1, col2 von DynamoDB: eine kommagetrennte Liste von Attributnamen, die GetItem, Query oder Scan sagt, nur diese Attribute zurückzugeben statt des ganzen Items.

Reduzieren DynamoDB Projection Expressions die Read-Kosten?

Nein. Eine ProjectionExpression stutzt das Response-Payload, nicht die Read Capacity, die dir in Rechnung gestellt wird. DynamoDB liest das volle Item aus dem Speicher, berechnet die anhand seiner Größe auf der Platte und verwirft erst danach die Attribute, die du nicht genannt hast. Um die Read-Kosten wirklich zu senken, verwende stattdessen einen Covering .

  • Sie stutzt das Payload, nicht die Read-Kosten. DynamoDB liest (und rechnet ab) das volle Item aus dem Speicher und droppt dann auf dem Weg hinaus die Attribute, die du nicht genannt hast. ProjectionExpression ist eine Netzwerk-Optimierung, keine Kapazitäts-Optimierung.
  • Sie ist, wie du eine öffentliche Teilmenge holst. Nenne die wenigen Attribute, die ein Aufrufer sehen darf; der Rest verlässt die Tabelle nie.
  • Verwende #name-Platzhalter für alles, was reserviert sein könnte. Bloße Attributnamen in der Expression kollidieren mit DynamoDBs ~570 reservierten Wörtern und lassen die Anfrage scheitern.
  • Für echte Read-Ersparnisse verwende stattdessen einen Covering Index. Ein GSI, der nur die benötigten Spalten projiziert, wird zu seiner eigenen (kleineren) Größe gelesen.

Was sie tatsächlich spart

Aus SQL kommend würdest du annehmen, dass SELECT a, b weniger scannt als SELECT *. In DynamoDB ist diese Intuition falsch. Die Capacity Unit für einen Read wird aus der Größe des Items auf der Platte berechnet, aufgerundet auf die nächsten 4 KB — bevor die Projektion angewandt wird. AWS ist explizit: eine ProjectionExpression ändert die Read Capacity nicht, die eine Anfrage verbraucht.1

Eine Projektion spart dir also zwei Dinge, beide real, aber beide nachgelagert zum Read:

  • Bytes über die Leitung. Ein 6-KB-Item, das als zwei kleine Attribute zurückkommt, ist eine winzige Response. Auf einer Query, die hunderte Items zurückgibt, summiert sich das schnell.
  • Clientseitige Arbeit. Weniger zu deserialisieren, weniger im Speicher zu halten, weniger versehentlich in ein Log oder eine API-Response zu lecken.

Was sie nicht spart, ist die RCU. Das ist die Falle: Leute greifen zu einer Projektion, um ihre Rechnung zu kürzen, sehen keine Änderung und schließen, dass DynamoDB kaputt ist. Ist es nicht — du hast den falschen Hebel gemessen.

Projiziere ein öffentliches User-Profil

Angenommen, du betreibst ein User-Verzeichnis. Jedes Profil ist ein Item, gekeyt, sodass du eine Person per Handle holen kannst:

PK = "PROFILE#ada"      (partition key)
SK = "PROFILE#ada"      (sort key — single-item collection)

Das Item ist dick. Es trägt das öffentliche Gesicht des Kontos plus einen Haufen privater und operativer Attribute:

{
  "PK": "PROFILE#ada",
  "SK": "PROFILE#ada",
  "displayName": "Ada L.",
  "avatarUrl": "https://cdn.example.com/u/ada.png",
  "bio": "Builds things.",
  "emailAddress": "ada@example.com",
  "passwordResetToken": "…",
  "billingCustomerId": "cus_…",
  "lastLoginIp": "…",
  "internalRiskScore": 0.02
}

Eine öffentliche Profilkarte braucht drei Felder. Das ganze Item zu holen bedeutet, dass emailAddress, lastLoginIp und internalRiskScore in einen Kontext reisen, der sie nie sehen sollte. Nenne nur die öffentliche Teilmenge:

GetItem  PK = "PROFILE#ada"  SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio

Die Response trägt drei Attribute. Die privaten bleiben in der Tabelle — nicht von deiner App nach Ankunft herausgefiltert, sondern überhaupt nie in die Response serialisiert. Das ist der Sicherheitsgewinn, und es ist der, der schwer rückgängig zu machen ist, sobald ein Geheimnis schon eine Grenze überschritten hat.

Du kannst diese exakte Anfrage — Namen, Platzhalter und den SDK-Aufruf — im DynamoDB Expression Builder zusammenbauen und kopieren, der die ProjectionExpression und die ExpressionAttributeNames-Map für dich emittiert.

Escape reservierte Wörter mit #-Platzhaltern

Hier sprengt eine saubere Projektion in die Luft. DynamoDB reserviert eine lange Liste von Wörtern — name, status, comment, size, timestamp und hunderte mehr.2 Wenn ein Attribut, das du projizierst, eines davon ist, wird der rohe Name in der Expression abgelehnt.

Angenommen, das Profil hat auch ein status-Attribut ("active", "suspended"). Das scheitert:

ProjectionExpression   displayName, status

status ist reserviert. Der Fix ist ein Expression Attribute Name — ein #-präfigierter Platzhalter, gemappt auf den echten Namen:

ProjectionExpression       displayName, #s
ExpressionAttributeNames   { "#s": "status" }

Derselbe Mechanismus reicht in verschachtelte Attribute. Um ein einzelnes Feld aus einer Map oder ein Element einer Liste zu ziehen, verwende Document-Path-Syntax — und platzhaltere jedes Segment, da jedes davon reserviert sein könnte:

ProjectionExpression       #addr.#city, tags[0]
ExpressionAttributeNames   { "#addr": "address", "#city": "city" }

Eine praktische Regel: platzhaltere alles. Du musst dir nie merken, auf welchem der ~570 reservierten Wörter du gerade stehst, und die Expression liest sich so oder so gleich.

Wann ein Covering Index eine Projektion schlägt

Wenn du wirklich die Read-Kosten kürzen musst — nicht nur das Payload — ist der Hebel ein Global Secondary Index, der nur die Attribute projiziert, die du liest. Ein GSI ist eine separate Kopie der Daten; du wählst KEYS_ONLY, INCLUDE oder ALL für seine Projektion.3 Ein KEYS_ONLY- oder schmaler INCLUDE-Index ist physisch kleiner pro Item, sodass eine Query dagegen zu dieser kleineren Größe gemessen wird.

Das ist ein Covering Index: die Query wird vollständig aus dem Index beantwortet, keine Reise zurück zur Basistabelle. Verwende ihn, wenn ein heißes Read-Muster nur je ein paar Attribute aus großen Items braucht.

ProjectionExpressionCovering GSI
Schneidet PayloadJaJa
Schneidet Read-KostenNeinJa — gelesen zur Größe des Index
Extra-SpeicherKeinerEine zweite Kopie der projizierten Felder
Extra-Write-KostenKeineWrites propagieren zum Index
Am besten fürPrivate Felder verstecken; kleine GewinneHeiße Reads weniger Felder aus großen Items

Der Trade-off ist ehrlich: der Index kostet dich Speicher und Write Capacity, um Read Capacity zu sparen. Lohnend für einen häufigen Read einer dünnen Scheibe aus einem schweren Item; nicht lohnend, um ein einmaliges GetItem zu rasieren. Siehe GSI vs. LSI für die Wahl des Indextyps und wann ein GSI-Read veraltet sein kann, bevor du einen auf den heißen Pfad legst.

Fallstricke und nächste Schritte

  • Erwarte keine kleinere Rechnung. Eine Projektion allein ändert nie die RCU. Wenn sich die Zahl nicht bewegt hat, ist das das dokumentierte Verhalten, kein Bug.
  • Platzhaltere reservierte Wörter. Ein bloßes name oder status in der Expression lässt die Anfrage scheitern — #-mappe es.
  • Key-Attribute zu projizieren ist gratis und oft nützlich — DynamoDB gibt sie billig zurück, und sie lassen dich paginieren oder neu holen.
  • Greif zu einem Covering Index nur, wenn ein heißes Muster ein paar Felder aus großen Items liest; wäge die Write-/Speicher-Kosten zuerst ab.

Baue die ProjectionExpression und ihre Attributnamen-Map im Expression Builder, und probier DynoTable, um diese Projektionen gegen deine eigenen Tabellen auszuführen und zuzusehen, wie die Response schrumpft.


  1. AWS DynamoDB Developer Guide, Working with Read and Write Operations — Read Capacity basiert auf der Item-Größe, bevor irgendeine ProjectionExpression angewandt wird. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/
  2. AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
  3. AWS DynamoDB Developer Guide, Attribute Projections (KEYS_ONLY / INCLUDE / ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

Aktualisiert