Projections d'index DynamoDB
Quand tu crées un secondary index, DynamoDB ne copie pas automatiquement tout l'item dedans. Tu choisis ce qui est copié — la projection de l'index. Choisis trop peu et tes queries paient une seconde lecture pour fetch le reste ; choisis tout et tu paies du stockage et du coût d'écriture extra à chaque update. C'est un tradeoff que tu settes une fois à la création de l'index et avec lequel tu vis.
(Ne confonds pas ça avec un projection expression, qui trim les attributs qu'une seule lecture renvoie. Cette page porte sur ce qu'un index stocke physiquement — vois projection expressions pour l'autre.)
Qu'est-ce qu'une projection d'index DynamoDB ?
Une projection est le set d'attributs que DynamoDB copie du table de base dans
un secondary index. Tu choisis un des trois types : KEYS_ONLY (juste les
clés), INCLUDE (clés plus une liste nommée d'attributs), ou ALL (tout
l'item). Plus de projection veut dire moins de fetches du table de base mais
plus de stockage et de coût d'écriture.
- Une projection est le set d'attributs copiés dans un secondary index.
KEYS_ONLY— seulement les clés du table et de l'index. Le plus petit, le moins cher.INCLUDE— les clés plus une liste nommée d'attributs extra que tu choisis.ALL— chaque attribut de l'item. Le plus grand ; les queries n'ont jamais besoin du table de base.- Un attribut qui n'est pas projeté est simplement indisponible depuis un GSI — ton app doit émettre ses propres lectures du table de base. (Seul un LSI fetch les attributs non projetés pour toi, à coût de lecture extra.)
- Plus de projection = plus de stockage + plus de coût d'écriture, puisque chaque écriture du table de base se propage à l'index.
Le problème : l'index qui te fait lire deux fois
Disons que tu gères un support desk avec un GSI qui te laisse lister les
tickets ouverts par priorité. Tu projettes KEYS_ONLY pour le garder lean.
La query revient vite — mais elle ne te donne que des IDs de ticket, et ton
écran de queue a besoin du subject, assignee et age de chaque ticket.
Donc maintenant ton code fait une seconde ronde de lectures contre le table de base pour hydrater chaque résultat. Le « one query » que tu as conçu est vraiment une query plus N gets, et la latence et le coût que tu essayais d'économiser sont revenus. La projection était trop mince pour le modèle d'accès.
Ce que chaque type de projection copie
KEYS_ONLYstocke juste la clé du table de base et la clé d'index. Utilise-le quand la query a seulement besoin de savoir quels items matchent et que tu fetcheras les détails ailleurs — ou pas du tout.INCLUDEstocke les clés plus une liste fixe d'attributs que tu nommes. Le sweet spot : projette exactement les fields dont ta query a besoin pour render, et rien de plus.ALLcopie l'item entier. Les queries sont fully self-served depuis l'index, au coût de dupliquer tout le stockage et write throughput de l'item dedans.
Pour la queue du support desk, INCLUDE avec subject, assignee et age
est le bon appel — la queue render depuis l'index seul, sans second fetch et
sans dupliquer le gros body du ticket dans l'index.
Le coût que tu trades
Chaque attribut que tu projettes est
stocké une seconde fois
et réécrit dans l'index chaque fois que l'item de base change. Donc une
projection ALL généreuse sur un table fréquemment updaté multiplie à la fois
stockage et write capacity. Projette ce que la query lit, pas « tout, juste
au cas où ».
Une subtilité à connaître : avec un index sparse, la projection ne tient
encore que les items qui portent la clé d'index — donc INCLUDE/ALL sur un
index sparse reste petit parce que l'index
lui-même est petit. Pèse le multiplicateur stockage et écriture pour ta
projection avec le
calculateur de pricing DynamoDB, et
assemble les queries d'index elles-mêmes avec
le DynamoDB expression builder.
Voir une projection dans DynoTable
DynoTable liste chacun des secondary indexes d'un table et te laisse query directement à travers un. Lance le même modèle d'accès contre le table de base et contre un GSI et compare les résultats — les attributs manquants du résultat d'index sont exactement ceux qu'il ne projette pas, donc l'effet d'une projection est visible sans relire la définition du table.

Pièges + étapes suivantes
- Un attribut non projeté sur un GSI veut dire un fetch du table de base — conçois la projection autour de ce que la query render.
ALLest rarement free — ça duplique stockage et coût d'écriture ; default versINCLUDEsauf si l'index a vraiment besoin de chaque field.- Les projections sont surtout fixes. Tu ne peux pas librement éditer la projection d'un GSI plus tard sans recreer l'index — choisis délibérément d'emblée.
- Lié : GSI vs LSI et indexes sparse façonnent combien une projection stocke vraiment.
Envie de voir ce que chacun de tes indexes renvoie vraiment avant de les redesigner ? Télécharge DynoTable et query tes tables directement.
Coût d'hydration : KEYS_ONLY + N gets
Retour à l'exemple de queue du support desk : 50 tickets ouverts affichés avec subject, assignee et age.
| Projection | Query d'index | Lectures de suivi | Esquisse RCU EC (items de base 2 KB) |
|---|---|---|---|
KEYS_ONLY | 50 clés renvoyées | 50 × GetItem | ~50 RCU index + ~50 RCU base |
INCLUDE subject, assignee, age | 50 lignes self-contained | none | ~50 RCU index seulement |
ALL | 50 copies full | none | ~50 RCU index ; amp stockage + écriture plus haute |
Les chiffres exacts dépendent des tailles d'attributs projetés — colle un
ticket d'exemple dans le
calculateur de taille d'item et
multiplie par la profondeur de queue. Un INCLUDE qui ne liste que les fields
UI bat souvent ALL quand l'attribut body est large et rarement montré dans
la list view.
Comportement de fetch de projection LSI
Seuls les LSIs peuvent optionnellement fetch des attributs non projetés
depuis le table de base pendant un query (avec un coût de lecture
supplémentaire). Les GSIs ne le font jamais — les attributs manquants
requièrent que ton application appelle GetItem sur le table de base. Cette
différence pousse beaucoup de designs GSI vers des projections INCLUDE un peu
plus larges d'emblée.
Changer les projections plus tard
Les projections GSI sont fixes à la création. Élargir KEYS_ONLY vers
INCLUDE requiert de créer un nouvel index, backfiller, cutover le trafic, et
supprimer l'ancien index — planifie les fields avant le launch. Les LSIs
partagent la même limitation.
Quand tu évalues un nouveau modèle d'accès, query l'index candidat dans DynoTable et liste quels attributs apparaissent — les gaps mappent 1:1 aux entrées de projection manquantes.
Paire avec les indexes sparse
Un GSI sparse qui indexe seulement les tickets status = open stocke des
projections pour les lignes ouvertes seulement. INCLUDE sur cet index reste
bon marché même quand le table de base tient des millions de tickets fermés —
l'index ne les a jamais copiés.
Combine avec les patterns d'index sparse quand le sous-ensemble filtré est petit par rapport au table.
Construis le modèle d'accès d'abord
Utilise le query builder pour prototyper la query GSI — key condition, projection expression et filter — avant d'altérer CloudFormation. Swap les types de projection dans la discussion de design en demandant quelles colonnes l'UI render ; tout le reste reste sur le table de base.


