Atomic Counter in DynamoDB
Un atomic counter è un attributo numerico che incrementi sul posto con una singola
chiamata UpdateItem — niente lettura prima, nessuna race read-modify-write. DynamoDB applica
ogni incremento in ordine di arrivo e non lascia mai che due writer si calpestino il
conteggio a vicenda.
Cos'è un atomic counter di DynamoDB?
Un atomic counter di DynamoDB è un attributo numerico che incrementi sul posto con una singola chiamata UpdateItem usando un'update expression ADD (o SET x = x + :n). DynamoDB legge, somma e scrive il valore server-side, così i writer concorrenti si serializzano senza aggiornamenti persi — ma non è idempotente, quindi una chiamata ritentata incrementa due volte.
- Usa
ADD(oSET x = x + :n) per incrementare in una chiamata. DynamoDB legge, somma e scrive server-side — i chiamanti concorrenti si serializzano, nessun aggiornamento perso. - Niente lettura prima. Venendo da SQL faresti
SELECTpoiUPDATE; qui salti del tutto la lettura e l'operazione è comunque sicura sotto concorrenza. - Gli atomic counter non sono idempotenti. Un
UpdateItemritentato incrementa di nuovo. Se non puoi tollerare conteggi in eccesso o in difetto, usa un conditional update. ADDsu un attributo mancante parte da 0, quindi il primo incremento funziona e basta — nessuna scrittura di seed necessaria.
Il problema con read-modify-write
Diciamo che tracci le visualizzazioni su un video. L'istinto ingenuo, dritto da SQL, è:
GetItem, aggiungi uno nella tua app, PutItem il nuovo totale.
Due spettatori premono play insieme. Entrambi leggono views = 41. Entrambi scrivono 42. Hai
contato una visualizzazione, non due. È un aggiornamento perso — il classico footgun di
concorrenza, e non si manifesta finché non hai traffico.
In SQL lo schiveresti con UPDATE videos SET views = views + 1, spingendo
l'aritmetica nel database. DynamoDB ha la stessa mossa, ed è proprio il punto
di un atomic counter.
Incrementa in una chiamata
Modella un Item di statistiche per video. Chiave di partizione VID#<id>, sort key STATS#TOTAL,
con un play_count numerico:
| PK | SK | play_count |
|---|---|---|
| "VID#9f3a" | "STATS#TOTAL" | 41 |
Per registrare un play, invia un solo 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 dentro una singola
operazione server-side. Non c'è finestra perché un altro writer si infili. Dieci
play concorrenti producono +10, ogni volta — è ciò che "atomic" ti compra.
Puoi costruire e copiare questa esatta espressione — nomi, valori e tutti e quattro i tipi di clausola — con il DynamoDB Expression Builder.
ADD funziona anche quando play_count non esiste ancora: DynamoDB tratta un attributo
numerico mancante come 0, quindi il primo play lo crea a 1. Nessuna scrittura di seed
separata. (AWS: Using update expressions)
ADD vs SET +: scegline uno
Due espressioni fanno la stessa aritmetica. AWS raccomanda SET per uso generale
perché si compone con altre azioni SET e si legge più esplicitamente. (AWS:
Using update expressions)
ADD play_count :one | SET play_count = play_count + :one | |
|---|---|---|
| Attr mancante | Lo crea, partendo da 0 | Errore — serve if_not_exists |
| Tipi di dato | Solo number e set | Number (e altro) via SET |
Combinare con SET | Clausola separata | Una clausola SET, separata da virgola |
| Indicazione AWS | Va bene per i counter | Default raccomandato |
Se l'attributo potrebbe non esistere e vuoi SET, proteggilo:
SET play_count = if_not_exists(play_count, :zero) + :one. Con ADD lo salti
— fa il seed da 0 gratis.
Fallo in DynoTable
Apri l'Item, modifica play_count, e puoi osservare un incremento atomico arrivare
senza scrivere a mano JSON — il pannello di update emette l'espressione ADD per te
e mostra il nuovo valore nel momento in cui fa il commit.
La trappola: i counter non sono idempotenti
Ecco la parte che morde i team in produzione. Un atomic counter incrementa
ogni volta che UpdateItem gira. (AWS: Working with items)
Immagina un blip di rete: invii l'incremento, la connessione cade prima che la risposta torni indietro, e non sai se è atterrato. Ritenti. Se la prima chiamata è riuscita, hai ora contato quel play due volte.
Per le visualizzazioni video va bene — qualche doppio conteggio su un milione di play non farà male a nessuno, e AWS chiama questo esatto caso "track visitors" l'uso canonico degli atomic counter. (AWS: Working with items)
Non va bene per nulla che debba essere esatto: inventario che puoi vendere in eccesso, crediti che puoi spendere due volte, un saldo che puoi corrompere. Lì, reggiungi a un conditional update.
Quando ti serve l'esattezza: conditional update
Un conditional update è idempotente se condizioni sullo stesso attributo che stai
cambiando. Incrementa 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 retry è sicuro: se la prima scrittura ha già portato play_count a 42, la
condizione play_count = 41 fallisce la seconda volta e nulla cambia. (AWS:
Working with items)
Il costo è la concorrenza. Due writer che corrono sulla stessa condizione significa che uno vince
e uno ottiene un ConditionalCheckFailedException da ritentare — hai scambiato il
throughput del counter incondizionato per la correttezza. Per counter esatti e contesi
è lo scambio giusto. Per i conteggi di visualizzazioni è eccessivo.
Errori
- Un Item caldo. Una singola riga di counter è una chiave di partizione. Un video virale
che martella
VID#9f3a/STATS#TOTALpuò colpire un tetto di scrittura per partizione. Fai lo shard: distribuisci le scritture suSTATS#TOTAL#0..Ne somma in lettura. - Niente incremento in batch.
BatchWriteItemè solo put/delete — non può eseguire update expression. I counter passano daUpdateItem, un Item per chiamata. ADDè solo number e set. Non tocca string o boolean; quello è unSET. Vedi tipi di dato DynamoDB per il modello completo degli attributi.
Prossimi passi
Gli atomic counter sono un pattern di scrittura; come leggi indietro gli aggregati è una
domanda di modellazione — vedi single-table design per tenere gli Item
di statistiche accanto al loro genitore, e Query vs Scan così che
fare il roll-up di un counter con sharding resti una Query.
Abbozza e copia l'incremento nel DynamoDB Expression Builder, poi prova DynoTable per eseguire update atomici sulle tue tabelle e osservare i conteggi muoversi.