Contadores Atômicos no DynamoDB
Um contador atômico é um atributo numérico que você incrementa no lugar com uma única
chamada UpdateItem — sem leitura antes, sem corrida read-modify-write. O DynamoDB
aplica cada incremento na ordem de chegada e nunca deixa dois escritores atropelarem a
contagem um do outro.
O que é um contador atômico no DynamoDB?
Um contador atômico no DynamoDB é um atributo numérico que você incrementa no lugar com uma única chamada UpdateItem, usando uma expressão de atualização ADD (ou SET x = x + :n). O DynamoDB lê, soma e escreve o valor no lado do servidor, então escritores concorrentes se serializam sem atualizações perdidas — mas ele não é idempotente, então uma chamada repetida incrementa duas vezes.
- Use
ADD(ouSET x = x + :n) para incrementar em uma chamada. O DynamoDB lê, soma e escreve no lado do servidor — chamadores concorrentes se serializam, sem atualizações perdidas. - Sem leitura antes. Vindo do SQL você faria
SELECTe depoisUPDATE; aqui você pula a leitura inteiramente e a operação ainda é segura sob concorrência. - Contadores atômicos não são idempotentes. Um
UpdateItemrepetido incrementa de novo. Se você não tolera contar a mais ou a menos, use uma atualização condicional. ADDem um atributo ausente começa em 0, então o primeiro incremento já funciona — sem escrita de semente necessária.
O problema com read-modify-write
Digamos que você acompanhe visualizações em um vídeo. O instinto ingênuo, direto do
SQL, é: GetItem, somar um no seu app, PutItem o novo total de volta.
Dois espectadores apertam play ao mesmo tempo. Ambos leem views = 41. Ambos escrevem
42. Você contou uma visualização, não duas. Essa é uma atualização perdida — a cilada
clássica de concorrência, e ela não aparece até você ter tráfego.
No SQL você a desviaria com UPDATE videos SET views = views + 1, empurrando a
aritmética para dentro do banco de dados. O DynamoDB tem o mesmo movimento, e é o
objetivo todo de um contador atômico.
Incremente em uma chamada
Modele um item de estatísticas por vídeo. Chave de partição VID#<id>, chave de
classificação STATS#TOTAL, com um play_count numérico:
| PK | SK | play_count |
|---|---|---|
| "VID#9f3a" | "STATS#TOTAL" | 41 |
Para registrar um play, envie um UpdateItem com uma cláusula ADD:
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression ADD play_count :one
Values :one = 1
O DynamoDB lê play_count, soma 1 e escreve o resultado dentro de uma única operação
no lado do servidor. Não há janela para outro escritor se infiltrar. Dez plays
concorrentes produzem +10, toda vez — é isso que "atômico" te compra.
Você pode montar e copiar essa expressão exata — nomes, valores e todos os quatro tipos de cláusula — com o DynamoDB Expression Builder.
ADD funciona mesmo quando play_count ainda não existe: o DynamoDB trata um atributo
numérico ausente como 0, então o primeiro play o cria em 1. Sem escrita de semente
separada. (AWS: usando expressões de atualização)
ADD vs SET +: escolha um
Duas expressões fazem a mesma aritmética. A AWS recomenda SET para uso geral porque
ele compõe com outras ações SET e lê de forma mais explícita. (AWS: usando
expressões de atualização)
ADD play_count :one | SET play_count = play_count + :one | |
|---|---|---|
| Atributo ausente | Cria-o, começando em 0 | Erro — precisa de if_not_exists |
| Tipos de dados | Só números e conjuntos | Números (e mais) via SET |
Combinar c/ SET | Cláusula separada | Uma cláusula SET, separada por vírgula |
| Orientação AWS | Ok para contadores | Padrão recomendado |
Se o atributo puder não existir e você quiser SET, proteja-o:
SET play_count = if_not_exists(play_count, :zero) + :one. Com ADD você pula isso —
ele semeia a partir de 0 de graça.
Faça isso no DynoTable
Abra o item, edite play_count, e você pode ver um incremento atômico acontecer sem
escrever JSON à mão — o painel de atualização emite a expressão ADD para você e mostra
o novo valor no momento em que ele é commitado.
A cilada: contadores não são idempotentes
Eis a parte que morde os times em produção. Um contador atômico incrementa toda vez
que o UpdateItem roda. (AWS: trabalhando com itens)
Imagine uma falha de rede: você envia o incremento, a conexão cai antes de a resposta voltar, e você não sabe se ele aconteceu. Você repete. Se a primeira chamada de fato teve sucesso, você agora contou aquele play duas vezes.
Para visualizações de vídeo isso está ok — algumas contagens duplas em um milhão de plays não vão machucar ninguém, e a AWS chama esse exato caso de "rastrear visitantes" de uso canônico dos contadores atômicos. (AWS: trabalhando com itens)
Não está ok para qualquer coisa que precise ser exata: estoque que você pode vender em excesso, créditos que você pode gastar em dobro, um saldo que você pode corromper. Aí, recorra a uma atualização condicional.
Quando você precisa de exatidão: atualizações condicionais
Uma atualização condicional é idempotente se você condiciona no mesmo atributo que está
mudando. Incremente play_count para 42, mas só se ele estiver atualmente em 41:
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression SET play_count = :next
ConditionExpression play_count = :current
Values :next = 42, :current = 41
Agora um retry é seguro: se a primeira escrita já moveu play_count para 42, a
condição play_count = 41 falha na segunda vez e nada muda. (AWS: trabalhando com
itens)
O custo é a concorrência. Dois escritores correndo na mesma condição significa que um
ganha e um recebe um ConditionalCheckFailedException para repetir — você trocou o
throughput do contador incondicional por correção. Para contadores exatos e disputados
essa é a troca certa. Para contagens de visualização é exagero.
Ciladas
- Um item quente. Uma única linha de contador é uma chave de partição. Um vídeo
viral martelando
VID#9f3a/STATS#TOTALpode atingir um teto de escrita por partição. Faça sharding: espalhe as escritas porSTATS#TOTAL#0..Ne some na leitura. - Sem incremento em lote.
BatchWriteItemé só put/delete — ele não pode rodar expressões de atualização. Contadores passam porUpdateItem, um item por chamada. ADDé só números e conjuntos. Ele não toca strings ou booleanos; isso é umSET. Veja tipos de dados do DynamoDB para o modelo de atributo completo.
Próximos passos
Contadores atômicos são um padrão de escrita; como você lê as agregações de volta é
uma questão de modelagem — veja single-table design para
manter itens de estatísticas ao lado do seu pai, e Query vs Scan
para que somar um contador com sharding continue sendo um Query.
Rascunhe e copie o incremento no DynamoDB Expression Builder, depois experimente o DynoTable para rodar atualizações atômicas contra suas próprias tabelas e ver as contagens se moverem.