Intermediário6 min de leitura

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 (ou SET 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 SELECT e depois UPDATE; 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 UpdateItem novamente. Se você não tolera contagem excessiva ou insuficiente, use um .
  • ADD em 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:

PKSKplay_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 :oneSET play_count = play_count + :one
Atributo ausenteCria, começando em 0Erros — precisa do if_not_exists
Tipos de dadosSomente números e conjuntosNúmeros (e mais) via SET
Combine com SETCláusula separadaUma cláusula SET, separada por vírgula
Orientação AWSMulta para balcõesPadrã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#TOTAL pode atingir um teto de gravação por partição. Fragmente: espalhe as escritas no STATS#TOTAL#0..N e some na leitura.
  • Sem incremento de lote. BatchWriteItem é apenas put/delete - não pode ser executado . Os contadores passam pelo UpdateItem, um item por chamada. Se você precisar bater vários contadores atomicamente, O TransactWriteItems executa 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 é um SET. 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ê 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.

Atualizado