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.
ProjectionExpressionist 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.
ProjectionExpression | Covering GSI | |
|---|---|---|
| Schneidet Payload | Ja | Ja |
| Schneidet Read-Kosten | Nein | Ja — gelesen zur Größe des Index |
| Extra-Speicher | Keiner | Eine zweite Kopie der projizierten Felder |
| Extra-Write-Kosten | Keine | Writes propagieren zum Index |
| Am besten für | Private Felder verstecken; kleine Gewinne | Heiß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
nameoderstatusin 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.
- AWS DynamoDB Developer Guide, Working with Read and Write Operations — Read Capacity basiert auf der Item-Größe, bevor irgendeine
ProjectionExpressionangewandt wird. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ↩ - AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html ↩
- AWS DynamoDB Developer Guide, Attribute Projections (
KEYS_ONLY/INCLUDE/ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html ↩