Contadores atômicos DynamoDB: como ADD funciona e quando não funciona
Um contador atômico é um atributo numérico que você coloca no lugar com um único
Chamada UpdateItem - sem leitura primeiro, sem corrida de leitura-modificação-gravação. DynamoDB se aplica
cada incremento na ordem de chegada e nunca permite que dois escritores derrotem um ao outro
contar.
O que é um contador atômico DynamoDB?
Um contador atômico DynamoDB é um atributo numérico que você incrementa com uma única chamada UpdateItem usando uma expressão de atualização ADD (ou SET x = x + :n). O DynamoDB lê, adiciona e grava o valor no lado do servidor, para que os gravadores simultâneos serializem sem perda de atualizações - mas não é idempotente, portanto, uma chamada repetida aumenta duas vezes.
- Use
ADD(ouSET x = x + :n) para incrementar em uma chamada. DynamoDB lê, adiciona e grava no lado do servidor - os chamadores simultâneos são serializados, sem perda de atualizações. - Não leia primeiro. Vindo do SQL você teria
SELECTe depoisUPDATE; aqui você pula a leitura completa e a operação ainda é segura sob simultaneidade. - Os contadores atômicos não são idempotentes. Uma nova tentativa de incrementos de
UpdateItemnovamente. Se você não tolera contagem excessiva ou insuficiente, use um . ADDem um atributo ausente começa em 0, então o primeiro incremento apenas funciona - nenhuma gravação inicial é necessária.
O problema com leitura-modificação-gravação
Digamos que você monitore as visualizações de um vídeo. O instinto ingênuo, direto do SQL, é:
GetItem, adicione um em seu aplicativo, PutItem o novo total de volta.
Dois espectadores apertaram o play ao mesmo tempo. Ambos lêem views = 41. Ambos escrevem 42. Você
contou uma visualização, não duas. Essa é uma atualização perdida – a simultaneidade clássica
arma de fogo, e ela não aparece até que você tenha trânsito.
No SQL você se esquivaria com o UPDATE videos SET views = views + 1, empurrando o
aritmética no banco de dados. DynamoDB tem o mesmo movimento e é o todo
ponto de um contador atômico.
Incremento 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 uma jogada, envie um UpdateItem com uma cláusula ADD:
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression ADD play_count :one
Values :one = 1
DynamoDB lê play_count, adiciona 1 e escreve o resultado dentro de um único
operação do lado do servidor. Não há janela para outro escritor entrar.
jogadas simultâneas produzem +10, sempre - é isso que "atômico" compra para você.
Você pode construir e copiar esta expressão exata – nomes, valores e todos os quatro tipos de cláusula - com o Construtor de expressão DynamoDB.
O ADD funciona mesmo quando o play_count ainda não existe: o DynamoDB trata uma falta
atributo numérico como 0, então a primeira reprodução o cria em 1. Nenhuma semente separada
escreva. ([AWS: Usando expressões de atualização][upd])
ADD vs SET +: escolha um
Duas expressões fazem a mesma aritmética. AWS recomenda SET para uso geral
porque compõe com outras ações do SET e lê de forma mais explícita. ([AWS:
Usando expressões de atualização][upd])
ADD play_count :one | SET play_count = play_count + :one | |
|---|---|---|
| Atributo ausente | Cria, começando em 0 | Erros — precisa do if_not_exists |
| Tipos de dados | Somente números e conjuntos | Números (e mais) via SET |
Combine com SET | Cláusula separada | Uma cláusula SET, separada por vírgula |
| Orientação AWS | Multa para balcões | Padrão recomendado |
Se o atributo não existir e você precisar do SET, proteja-o:
SET play_count = if_not_exists(play_count, :zero) + :one. Com ADD você pula
isso - ele semeia de 0 gratuitamente.
Escreva o custo de cada incremento
Sob demanda no us-east-1, cada UpdateItem com um ADD cobra 1 WCU por
KB do tamanho do item após a gravação (arredondado). Uma linha de estatísticas de 900 bytes
custa 1 WCU por jogada registrada; dez jogadas simultâneas ainda resultam em 10
WCU total, não um. Fragmentar o contador entre partições move o
teto de rendimento sem alterar a matemática do WCU por item. Dimensione a linha com o
calculadora de tamanho de item e taxa de linha quente
caminhos na calculadora de preços.
Faça isso no DynoTable
Abra o item de estatísticas para inspecionar o contador ativo e, em seguida, crie um contador fragmentado
com SUM e GROUP BY no SQL Workbench para ver o total em cada
Linha STATS#TOTAL#0..N. Para esboçar o incremento em si, use a web
DynamoDB Expression Builder para compor o
ADD Expressão UpdateItem, nomes e valores incluídos.
A armadilha: os contadores não são idempotentes
Um contador atômico aumenta cada vez que o UpdateItem é executado. (AWS: Funcionando
com itens)
Imagine um sinal de rede: você envia o incremento, a conexão cai antes do a resposta volta e você não sabe se ela chegou. Você tenta novamente. Se o primeira chamada foi bem-sucedida, agora você contou essa jogada duas vezes.
Para visualizações de vídeo, tudo bem - algumas contagens duplas em um milhão de reproduções não farão mal qualquer pessoa, e AWS chama esse caso exato de "rastrear visitantes" de uso canônico de contadores atômicos. (AWS: Trabalhando com itens)
Não é adequado para qualquer coisa que deva ser exata: estoque que você pode vender em excesso, créditos que você pode gastar em dobro, um saldo que você pode corromper. Aí, pegue um atualização condicional.
Quando você precisa de exatidão: atualizações condicionais
Uma atualização condicional é idempotente se você condicionar o mesmo atributo que está
mudando. Aumente play_count para 42, mas somente se for atualmente 41:
# UpdateItem
Key PK = "VID#9f3a", SK = "STATS#TOTAL"
UpdateExpression SET play_count = :next
ConditionExpression play_count = :current
Values :next = 42, :current = 41
Agora uma nova tentativa é segura: se a primeira gravação já moveu play_count para 42, o
condição play_count = 41 falha pela segunda vez e nada muda. (AWS:
Trabalhando com itens)
O custo é a simultaneidade. Dois escritores competindo nas mesmas condições significa que um vence
e alguém recebe um ConditionalCheckFailedException para tentar novamente - você trocou o
taxa de transferência incondicional do contador para correção. Para ser exato, argumentou
contadores esse é o comércio certo. Para contagem de visualizações, é um exagero.
Armadilhas
- Um . Uma única linha de contador é uma chave de partição. Um vídeo viral
martelar
VID#9f3a/STATS#TOTALpode atingir um teto de gravação por partição. Fragmente: espalhe as escritas noSTATS#TOTAL#0..Ne some na leitura. - Sem incremento de lote.
BatchWriteItemé apenas put/delete - não pode ser executado . Os contadores passam peloUpdateItem, um item por chamada. Se você precisar bater vários contadores atomicamente, OTransactWriteItemsexecuta ações de atualização em até 100 itens em uma solicitação, em aproximadamente o dobro do custo de gravação. ADDé apenas números e conjuntos. Não toca strings ou booleanos; isso é umSET. Consulte Tipos de dados DynamoDB para obter informações completas modelo de atributos.
Próximos passos
Os contadores atômicos são um padrão de gravação; como você lê os agregados de volta é uma modelagem
pergunta - consulte design de tabela única para manter
itens de estatísticas ao lado de seu pai e Query vs Scan para
enrolar um contador fragmentado permanece um Query.
Rascunhe e copie o incremento no Construtor de expressão DynamoDB e, em seguida, experimente DynoTable para executar atualizações atômicas em suas próprias tabelas e observe o movimento das contagens.