Avanzato8 min di lettura

Come viene memorizzato internamente un GSI di DynamoDB

Un Global Secondary Index non è un puntatore di ritorno nella tua tabella. È una tabella separata, gestita internamente — le sue partizioni, il suo key schema, la sua capacità — che DynamoDB mantiene sincronizzata copiandovi le scritture in modo asincrono.

Venendo da SQL, un index è un B-tree imbullonato sulla stessa tabella fisica, aggiornato dentro la stessa transazione. Un GSI rompe entrambe quelle assunzioni, e quasi ogni sorpresa con un GSI risale a quell'unico fatto.

Come viene memorizzato un GSI di DynamoDB?

Un GSI di DynamoDB è memorizzato come una tabella separata e gestita internamente — con le proprie partizioni, il proprio key schema e la propria capacità — non come un puntatore nella tabella base. DynamoDB copia ogni scrittura nell'index in modo asincrono, memorizzando solo le chiavi del GSI, le chiavi della tabella base e gli eventuali attributi proiettati.

  • Un GSI è una tabella a sé. Ha uno spazio di partizione completamente indipendente, con chiave la partition key del GSI, non quella della tabella base.
  • Le scritture si replicano in modo asincrono. La tua scrittura viene committata prima sulla tabella base, poi DynamoDB la diffonde a ogni GSI su un percorso in background.
  • Vengono memorizzati solo gli attributi proiettati. L'index contiene le chiavi del GSI, le chiavi base, più qualunque attributo tu abbia proiettato — nient'altro.
  • La chiave del GSI non deve essere univoca. Più item base possono condividere una partition/sort key del GSI; la primary key della base è il discriminante che li mantiene distinti.

Inizia con un item base

Prendi un audit log SaaS. Ogni azione privilegiata in un workspace diventa un evento immutabile. La tabella base, WorkspaceEvents, ha le chiavi impostate così che tutti gli eventi di un workspace vivano in un'unica item collection, ordinati per tempo:

WorkspaceEvents (base table)
EventPKEventSKactorIdverbtargetRef
WS#orbit-9TS#2026-06-23T14:02:11ZUSR#kpROLE_GRANTEDUSR#mara

EventPK = "WS#orbit-9" partiziona per workspace; EventSK è un timestamp ISO così che una Query restituisca gli eventi di un workspace in ordine cronologico. Questo serve "mostrami la timeline di questo workspace" perfettamente.

Non serve nient'altro. Non puoi chiedere "cosa ha fatto USR#kp su ogni workspace?" — actorId non è una chiave, quindi l'unico modo per rispondere sulla tabella base è uno Scan completo. È l'access pattern che un GSI esiste per aggiungere.

Aggiungi un GSI e guarda apparire una seconda tabella

Definisci un GSI, ByActor, che ri-partiziona gli stessi eventi per chi li ha eseguiti:

ByActor (GSI)
GSI1PK = actorId   ("USR#kp")
GSI1SK = EventSK   ("TS#2026-06-23T14:02:11Z")

DynamoDB ora mantiene una seconda struttura fisica. Lo stesso evento logico è memorizzato due volte — una nella partizione WS#orbit-9 della tabella base, e di nuovo nella partizione USR#kp del GSI:

ByActor (GSI) — il suo spazio di partizione
GSI1PKGSI1SKEventPKEventSKverb
USR#kpTS#2026-06-23T14:02:11ZWS#orbit-9TS#2026-06-23T14:02:11ZROLE_GRANTED

Nota cosa è venuto al seguito: le chiavi della tabella base (EventPK, EventSK) sono memorizzate automaticamente in ogni item del GSI. È così che un hit sul GSI può rimandarti all'item completo — ed è perché un index KEYS_ONLY costa comunque storage.

Cosa vive davvero nel GSI

L'index non copia l'intero item. Ogni voce del GSI contiene esattamente tre cose, e tu controlli solo la terza:

Memorizzato nel GSIDa dove vieneOpzionale?
Partition + sort key GSIGli attributi che hai nominato come chiavi GSINo
Chiave/i tabella baseCopiata da ogni item baseNo
Attributi proiettatiLa tua scelta di Projection

Projection è KEYS_ONLY, INCLUDE (una lista nominata) o ALL. Una Query sul GSI può restituire solo attributi che sono nell'index.

Chiedine uno che non è proiettato e DynamoDB non lo recupera in modo trasparente — non ottieni nulla per quel campo. (documentazione AWS sui GSI)

È la trappola relazionale al contrario: SQL farebbe la join di ritorno all'heap per la colonna mancante. Un GSI non lo fa mai. La proiezione è l'intero contratto.

Come una scrittura raggiunge l'index

La replica è la parte che rompe più duramente l'intuizione SQL. Una scrittura base e il suo aggiornamento dell'index non sono un'unica operazione atomica.

Quando fai PutItem, DynamoDB committa durevolmente sulla tabella base, conferma la tua scrittura e poi propaga la modifica su un percorso in background che aggiorna ogni GSI. La conferma non aspetta l'index.

Ecco l'ordine degli eventi per la nostra scrittura di audit, dall'alto al basso:

PutItemevento WS#orbit-9Commit sullapartizione base200 OKal chiamantePercorso async:estrai le chiavi GSIInstrada alla partizioneUSR#kp di ByActorScrivi gli attributiproiettati

Il chiamante ottiene il suo 200 OK al terzo passo, prima che i passi da quattro a sei finiscano — quindi una Query su ByActor nell'intervallo può perdere un evento appena creato.

Quell'asincronia è per design, non un difetto: è la discendenza del paper Amazon Dynamo del 2007, che scelse la disponibilità rispetto alla coerenza sincrona. Le conseguenze complete vivono in perché un GSI è eventualmente coerente.

La chiave del GSI non è una chiave univoca

In SQL, un index secondario non univoco è il default e uno univoco è un vincolo a cui aderisci. Un GSI è l'opposto: non ha alcuna garanzia di univocità, mai.

Due eventi di audit dello stesso attore con timestamp che collidono condividerebbero lo stesso GSI1PK e GSI1SK. DynamoDB li memorizza entrambi — li disambigua internamente tramite la primary key della tabella base, che è sempre portata al seguito.

Quindi una Query sul GSI per un attore in un istante può legittimamente restituire diversi item. Se hai assunto una-riga-per-chiave come ti darebbe un index univoco SQL, quello è il footgun.

Quando interroghi l'index, l'Expression Builder per DynamoDB scrive la KeyConditionExpression con nomi e valori correttamente escaped — es. corrispondendo a un attore da un cutoff:

KeyConditionExpression: "#a = :actor AND #ts > :since"
ExpressionAttributeNames:  { "#a": "actorId", "#ts": "EventSK" }
ExpressionAttributeValues: {
  ":actor": { "S": "USR#kp" },
  ":since": { "S": "TS#2026-06-01T00:00:00Z" }
}

La capacità vive con l'index, non con la tabella

Poiché il GSI è una tabella a sé, ha la sua capacità di lettura e scrittura, fatturata e sottoposta a throttling separatamente dalla tabella base. Una lettura da ByActor consuma le unità di lettura del GSI, mai quelle della tabella.

L'accoppiamento inverso è quello che morde: ogni scrittura sulla tabella base scrive anche l'index, e se il GSI non riesce ad assorbirla, fa back-pressure sulla scrittura base. Quel meccanismo ha la sua guida — quando un GSI va in throttling sulle scritture della tabella base.

Questo è anche perché la partition key di un GSI conta tanto quanto quella della tabella base. Una chiave GSI a bassa cardinalità raggruppa le scritture su un'unica partizione dell'index anche quando le scritture base sono perfettamente distribuite — una hot partition che ti sei creato re-impostando le chiavi.

Insidie e prossimi passi

  • Non aspettarti il ritorno di attributi non proiettati. Una Query sul GSI restituisce solo ciò che l'index memorizza. Se ti serve l'item completo, proiettalo o recuperalo dalla tabella base tramite le chiavi portate al seguito.
  • Non trattare una chiave GSI come univoca. Pianifica che una Query restituisca più di un item per chiave; la primary key della base è l'unica vera identità.
  • Non leggere un GSI subito dopo la scrittura che l'ha alimentato. Il percorso async significa che l'index potrebbe non mostrare ancora la tua scrittura — leggi la tabella base quando ti serve il read-your-own-writes.
  • Dimensiona la capacità del GSI deliberatamente. È indipendente sulle letture e una dipendenza nascosta sulle scritture.

L'intero gioco è scegliere forme di chiave che servano i tuoi pattern — il single-table design sovraccarica un GSI su molti di essi; GSI vs LSI copre quando un index locale calza invece.

Costruisci e visualizza in anteprima la tua KeyConditionExpression del GSI nell'Expression Builder per DynamoDB, poi prova DynoTable per ispezionare gli attributi proiettati di un index e guardare le scritture replicarsi nel GSI sulle tue tabelle.

Aggiornato