Intermedio6 min di lettura

Contatori atomici DynamoDB: come funziona ADD e quando non funziona

Un contatore atomico è un attributo numerico che metti in posizione con un singolo Chiamata UpdateItem: nessuna lettura prima, nessuna corsa di lettura-modifica-scrittura. Si applica DynamoDB ogni incremento nell'ordine di arrivo e non consente mai a due scrittori di ostacolarsi a vicenda contare.

Cos'è un contatore atomico DynamoDB?

Un contatore atomico DynamoDB è un attributo numerico che si incrementa con una singola chiamata UpdateItem utilizzando un'espressione di aggiornamento ADD (o SET x = x + :n). DynamoDB legge, aggiunge e scrive il valore lato server, quindi gli scrittori simultanei serializzano senza perdere aggiornamenti, ma non è idempotente, quindi una chiamata ripetuta aumenta due volte.

  • Utilizzare ADD (o SET x = x + :n) per incrementare in una chiamata. DynamoDB legge, aggiunge e scrive lato server: i chiamanti simultanei serializzano, senza perdere aggiornamenti.
  • Non leggere prima. Provenendo da SQL diresti SELECT e poi UPDATE; qui salti la lettura è completa e l'operazione è ancora sicura in concorrenza.
  • I contatori atomici non sono idempotenti. Un nuovo tentativo di UpdateItem aumenta di nuovo. Se non riesci a tollerare il conteggio eccessivo o insufficiente, utilizza un .
  • ADD su un attributo mancante inizia da 0, quindi solo il primo incremento funziona: non è necessaria la scrittura seed.

Il problema con lettura-modifica-scrittura

Supponiamo che tu monitori le visualizzazioni di un video. L'istinto ingenuo, direttamente da SQL, è: GetItem, aggiungine uno nella tua app, PutItem il nuovo total back.

Due spettatori avviano la riproduzione contemporaneamente. Entrambi leggono views = 41. Entrambi scrivono 42. Tu contato una visualizzazione, non due. Questo è un aggiornamento perduto: la classica concorrenza footgun e non viene visualizzato finché non c'è traffico.

In SQL lo schiveresti con UPDATE videos SET views = views + 1, spingendo il aritmetica nel database. DynamoDB ha la stessa mossa, ed è il tutto punto di un contatore atomico.

Incremento in una chiamata

Modella un elemento statistico per video. partition key VID#<id>, chiave di ordinamento STATS#TOTAL, con un play_count numerico:

PKSKplay_count
"VID#9f3a""STATS#TOTAL"41

Per registrare una giocata, invia un UpdateItem con una clausola ADD:

# UpdateItem
Key               PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression  ADD play_count :one
Values            :one = 1

DynamoDB legge play_count, aggiunge 1 e scrive il risultato all'interno di un singolo funzionamento lato server. Non c'è alcuna finestra in cui un altro scrittore possa infilarsi. Dieci le riproduzioni simultanee producono +10, ogni volta: questo è ciò che ti offre "atomico".

Puoi creare e copiare questa espressione esatta: nomi, valori e tutti e quattro tipi di clausola - con il Creatore di espressioni DynamoDB.

ADD funziona anche quando play_count non esiste ancora: DynamoDB tratta un mancante attributo numerico come 0, quindi la prima riproduzione lo crea in 1. Nessun seme separato scrivere. (AWS: utilizzo delle espressioni di aggiornamento)

ADD vs SET +: scegline uno

Due espressioni eseguono la stessa operazione aritmetica. AWS consiglia SET per uso generale perché si compone con altre azioni SET e legge in modo più esplicito. ([AWS: Utilizzo delle espressioni di aggiornamento[upd])

ADD play_count :oneSET play_count = play_count + :one
Attr mancanteLo crea, a partire da 0Errori: richiede if_not_exists
Tipi di datiSolo numeri e insiemiNumeri (e altro) tramite SET
Combina con SETClausola separataUna clausola SET, separata da virgole
Guida AWSMulta per i contatoriPredefinito consigliato

Se l'attributo potrebbe non esistere e desideri SET, proteggilo: SET play_count = if_not_exists(play_count, :zero) + :one. Con ADD salti quello: semina da 0 gratuitamente.

Scrivi il costo di ogni incremento

Su richiesta in us-east-1, ogni UpdateItem con un ADD fattura 1 WCU per KB della dimensione dell'elemento dopo la scrittura (arrotondato per eccesso). Una riga di statistiche da 900 byte costa 1 WCU per partita registrata; dieci giocate simultanee risultano ancora 10 WCU totale, non uno. La suddivisione del contatore tra le partizioni sposta il file limite massimo di throughput senza modificare la matematica WCU per articolo. Dimensiona la riga con calcolatore delle dimensioni dell'articolo e velocità di linea eccezionale percorsi nel calcolatore dei prezzi.

Fallo in DynoTable

Apri l'elemento delle statistiche per ispezionare il contatore live, quindi arrotola un contatore frammentato con SUM e GROUP BY nell'SQL Workbench per vedere il totale su ogni Riga STATS#TOTAL#0..N. Per redigere l'incremento stesso, utilizzare il web DynamoDB Expression Builder per comporre il file ADD Espressione UpdateItem, nomi e valori inclusi.

La trappola: i contatori non sono idempotenti

Un contatore atomico incrementa ogni volta che UpdateItem viene eseguito. ([AWS: funzionante con articoliarticoli)

Immagina un blip di rete: invii l'incremento, la connessione si interrompe prima del la risposta ritorna e non sai se è andata a buon fine. Riprova. Se il la prima chiamata ha avuto successo, ora hai contato quella giocata due volte.

Per le visualizzazioni video va bene: qualche conteggio doppio su un milione di riproduzioni non farà male chiunque, e AWS chiama questo esatto caso di "tracciamento dei visitatori" l'uso canonico di contatori atomici. (AWS: Lavorare con gli elementi)

Non va bene per tutto ciò che deve essere esatto: inventario che puoi vendere in eccesso, crediti che puoi spendere due volte, un saldo che puoi corrompere. Lì, prendi a aggiornamento condizionale.

Quando serve precisione: aggiornamenti condizionati

Un aggiornamento condizionale è idempotente se condiziona sullo stesso attributo che sei cambiando. Incrementa da play_count a 42, ma solo se attualmente è 41:

# UpdateItem
Key                  PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression     SET play_count = :next
ConditionExpression  play_count = :current
Values               :next = 42, :current = 41

Ora un nuovo tentativo è sicuro: se la prima scrittura ha già spostato play_count su 42, il file condizione play_count = 41 fallisce la seconda volta e non cambia nulla. ([AWS: Lavorare con gli elementi[elementi])

Il costo è la concorrenza. Due scrittori che gareggiano nella stessa condizione significa che uno vince e si ottiene un ConditionalCheckFailedException per riprovare: hai scambiato il throughput del contatore incondizionato per correttezza. Per l'esattezza, sostenuto ribatte che è il mestiere giusto. Per quanto riguarda le visualizzazioni è eccessivo.

Insidie

  • Un . Una singola riga di contatore corrisponde a una chiave di partizione. Un video virale martellare VID#9f3a / STATS#TOTAL può raggiungere un limite di scrittura per partizione. Suddividilo: distribuisci le scritture su STATS#TOTAL#0..N e somma in lettura.
  • Nessun incremento batch. BatchWriteItem è solo put/delete: non può essere eseguito . I contatori passano attraverso UpdateItem, un articolo per chiamata. Se devi colpire atomicamente diversi segnalini, TransactWriteItems esegue azioni di aggiornamento su un massimo di 100 elementi in una richiesta, a circa il doppio del costo di scrittura.
  • ADD è composto solo da numeri e insiemi. Non toccherà stringhe o valori booleani; quello è un SET. Per la versione completa vedere Tipi di dati DynamoDB. modello di attributi.

Passaggi successivi

I contatori atomici sono un modello di scrittura; il modo in cui leggi gli aggregati è un modello domanda: vedere progettazione a tabella singola per mantenerla elementi delle statistiche accanto al genitore e Query vs Scan quindi arrotolando un contatore frammentato rimane un Query.

Disegna e copia l'incremento nel file Builder di espressioni DynamoDB, quindi prova DynoTable per eseguire aggiornamenti atomici sulle tue tabelle e guardare i conteggi muoversi.

Aggiornato