Intermedio6 min di lettura

DynamoDB Espressioni di condizioni: la guida completa (con esempi)

Un'espressione condizionale è un predicato che DynamoDB valuta sull'elemento esistente prima che impegni la tua scrittura. Se il predicato è falso, la scrittura lo è rifiutato e non cambia nulla. È la cosa più vicina che DynamoDB abbia ad a Clausola "WHERE" su una scrittura - e l'unico modo sicuro per imporre un invariante.

Come funzionano le espressioni condizionali DynamoDB?

Un'espressione condizionale è un predicato DynamoDB valuta il lato server rispetto all'elemento corrente prima di eseguire una scrittura. Se è vero, la scrittura procede; se false, la scrittura viene rifiutata con "ConditionalCheckFailedException" e non cambia nulla. Riunisce l'assegno e la mutazione in un'unica operazione atomica, in modo che i chiamanti simultanei non possano eseguire una lettura obsoleta.

  • È una guardia, non un filtro. ConditionExpression viene eseguito lato server sul elemento corrente; un risultato falso fallisce la scrittura con "ConditionalCheckFailedException".
  • Sostituisce read-then-write. Nessun viaggio di andata e ritorno SELECT quindi UPDATE: il il controllo e la mutazione sono un'operazione atomica, quindi due chiamanti non possono gareggiare.
  • Il rifiuto è gratuito, non l'esecuzione gratuita. Ancora una scrittura condizionale non riuscita consuma capacità di scrittura. Una fattura di scrittura rifiutata WCUs per la dimensione del elemento esistente rispetto al quale è stato effettuato il controllo (minimo 1): creazione se assente non riuscita costa 1 WCU.

Provenendo da SQL, dovresti leggere la riga, controllarla nel codice dell'app, quindi aggiornare. Dentro DynamoDB quel divario tra lettura e scrittura è un bug di corruzione dei dati in attesa di a chiamante simultaneo. L'espressione della condizione colma il divario.

Dove si applicano

Allega ConditionExpression a PutItem, UpdateItem, DeleteItem e ogni azione all'interno di TransactWriteItems. Non fa parte di Query o Scan — quelli usano FilterExpression, che è una cosa diversa nel percorso di lettura.

Questa distinzione fa inciampare le persone, quindi sii preciso:

ConditionExpressionFilterExpression
PercorsoScrive (Put/Aggiorna/Elimina)Legge (Query/Scan)
Effetto sul fallimentoRifiuta l'intera scritturaElimina l'elemento dai risultati
VedeL'elemento corrente, prescriviOgni elemento candidato, post-letto
CostoImpossibile scrivere ancora fattureGli elementi filtrati vengono comunque fatturati per la lettura

Entrambi funzionano lato server. La differenza sta in ciò che fa "falso": una condizione interrompe a mutazione; un filtro nasconde semplicemente una riga che hai già pagato per leggere. (AWS: Espressioni condizione)

Le funzioni che utilizzerai effettivamente

La lingua della condizione è piccola. I cavalli da lavoro:

  • attribute_exists(path) / attribute_not_exists(path) — fa questo

esiste sull'articolo? Il classico modo di dire "crea solo se assente" / "aggiorna". solo se presente".

  • Comparatori — =, <>, <, <=, >, >= — rispetto a un valore o un altro attributo.
  • attribute_type, begins_with, contains, size — controlli di tipo e stringa/insieme.
  • "TRA... E...", "IN (...)" — portata e appartenenza.
  • AND, OR, NOT, parentesi - per combinare quanto sopra.

"attributo_non_esiste" sul fileè il modo canonico di fare PutItem si comporta come un inserto che non bloccherà un elemento esistente — DynamoDB non ha un'operazione di "inserimento" separata, quindi la condizione è la semantica di inserimento. (AWS: Riferimento per operatori e funzioni di confronto)

Un esempio pratico: proteggere un registro dagli scoperti di conto

Prendi un registro bancario. Ogni account è un elemento:

PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents    = 0

Un addebito non deve mai spingere il saldo disponibile sotto lo zero, e tu non deve mai addebitare un conto che non esiste. Due regole, entrambe applicabili nel la scrittura stessa.

Nel modo sbagliato (il fucile)

GetItem ACCT#a7f3 / BALANCE     → clearedCents = 50000
if (50000 >= 30000) ...         ← app-side check
UpdateItem  SET clearedCents = 20000

Tra GetItem e UpdateItem, un secondo addebito può leggere lo stesso "50000", supera il proprio controllo e scrivi anche tu. Entrambi riescono; il conto va negativo. Questa è una gara di lettura-modifica-scrittura e nessuna quantità di convalida lato app risolve il problema: il controllo e la scrittura sono operazioni separate.

Nel modo giusto

Piega l'assegno nella scrittura. Addebito 30000 centesimi, condizionato sul conto esistente e contenendo abbastanza:

UpdateItem  ACCT#a7f3 / BALANCE
  SET clearedCents = clearedCents - :amt
  ConditionExpression:
    attribute_exists(PK) AND clearedCents >= :amt

con :amt = 30000. Se il saldo è troppo basso o l'articolo non è mai stato creato, DynamoDB rifiuta la scrittura con ConditionalCheckFailedException e l'equilibrio è intatto. L'addebito simultaneo vede il saldo originale e lo è confrontato con esso o vede quello aggiornato: mai una lettura obsoleta su cui ha agito.

Puoi creare e copiare l'espressione esatta (nomi, valori e tutto il resto) con il file DynamoDB generatore di espressioni invece di assemblando manualmente la mappa "ExpressionAttributeValues".

Provalo qui: questo builder è preimpostato su un PutItem sorvegliato (attribute_not_exists) in modo da poter leggere il ConditionExpression generato:

Costruisci la tua richiesta
Codice generato
new PutItemCommand({
  "TableName": "AuditLog",
  "Item": {
    "pk": {
      "S": "TENANT#acme"
    },
    "sk": {
      "S": "EVENT#2026-06-24T10:00:00Z"
    },
    "action": {
      "S": "login"
    }
  },
  "ConditionExpression": "attribute_not_exists(#cond0)",
  "ExpressionAttributeNames": {
    "#cond0": "pk"
  }
})

Ispezione della guardia in DynoTable

Quando una scrittura condizionale fallisce, vuoi vedere lo stato reale dell'elemento, non indovinare a questo. Apri la voce dell'account e leggi direttamente "clearedCents".

La raccolta del registro in DynoTable: la voce SALDO mostra i centesimi liquidati sopra le voci delle transazioni del conto.
La raccolta del registro in DynoTable: la voce SALDO mostra i centesimi liquidati sopra le voci delle transazioni del conto.

Leggi il rifiuto, non riprovare alla cieca

"ConditionalCheckFailedException" non è un errore temporaneo: si riprova lo stesso scrivere non cambia nulla. Significa che viene applicata una regola aziendale: fondi insufficienti, creazione duplicata, versione obsoleta. Emergetelo come un risultato di dominio, non come un'infrastruttura blip.

Due cose rendono gli errori di cui è possibile eseguire il debug:

  • ReturnValuesOnConditionCheckFailure: ALL_OLD — DynamoDB restituisce il elemento corrente accanto all'errore, in modo da poter mostrare "il saldo era 20000, tu chiesto 30000" senza una seconda lettura. (AWS: Lavorare con gli elementi)
  • Distinguere i due motivi dell'errore. attribute_exists(PK) AND clearedCents >= :amt comprime "nessun account" e "nessun fondo" in uno solo eccezione. Se i chiamanti hanno bisogno di distinguerli, dividili in due, scrivi o controlla l'articolo restituito.

Il blocco ottimistico è lo stesso trucco

Il modello del numero di versione è solo un'espressione di condizione che indossa un file diverso cappello. Memorizza un attributo version; ogni scrittura afferma la versione che hai letto e lo urta:

UpdateItem  ACCT#a7f3 / BALANCE
  SET clearedCents = :new, version = :next
  ConditionExpression: version = :seen

Se un altro scrittore si è mosso per primo, version = :seen è falso, la scrittura viene rifiutata, e tu rileggi e riprovi. Ecco come DynamoDB controlla la concorrenza senza serrature: afferma ciò che hai visto, fallisci se si muoveva. (AWS: Blocco ottimistico con Numero versione) L'area di staging di DynoTable esegue questo schema per te: a la modifica simultanea emerge come un conflitto da risolvere, non come una scrittura persa.

Insidie e passaggi successivi

  • Nomi che entrano in conflitto con parole riservate. status, size, name e ~570 gli altri sono riservati. Alimentali con ExpressionAttributeNames (#s = status) oppure la richiesta viene rifiutata con una ValidationException ('Il nome dell'attributo è a parola chiave riservata'). Il reserved-words-checker prende il tuo i nomi degli attributi e restituisce la mappa degli alias pronta per essere incollata.
  • Una condizione non può fare riferimento a un altro elemento. Vede solo l'elemento in corso scritto. Le invarianti tra elementi necessitano di TransactWriteItems con una per azione ConditionExpression o un ConditionCheck contro un elemento sentinella.
  • Le scritture fallite costano comunque WCUs. Una guardia che rifiuta ancora il 90% delle volte fatture per quei rifiuti. Assicurazione economica, ma non gratuita.

Per modellare le chiavi contro cui corrono queste guardie, vedere design a tabella singola e Query vs Scan. Quando sei pronto a rilasciare il condizionale scrive rispetto a dati reali, scarica DynoTable ed eseguili rispetto i tuoi tabelle

Aggiornato