Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Transações e instrução de observação no Redis


Há várias perguntas aqui.

1) Por que não podemos executar o incremento na transação que não pode ser interrompida por outro comando?

Observe primeiro que as "transações" do Redis são completamente diferentes do que a maioria das pessoas pensa que as transações são no DBMS clássico.
# Does not work
redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec()

Você precisa entender o que é executado no lado do servidor (no Redis) e o que é executado no lado do cliente (no seu script). No código acima, os comandos GET e SET serão executados no lado do Redis, mas a atribuição de current e o cálculo de current + 1 devem ser executados no lado do cliente.

Para garantir a atomicidade, um bloco MULTI/EXEC atrasa a execução dos comandos Redis até o exec. Assim, o cliente apenas acumulará os comandos GET e SET na memória e os executará de uma só vez e atomicamente no final. É claro que a tentativa de atribuir corrente ao resultado de GET e incrementação ocorrerá bem antes. Na verdade, o método redis.get retornará apenas a string "QUEUED" para sinalizar que o comando está atrasado e o incremento não funcionará.

Nos blocos MULTI/EXEC você só pode usar comandos cujos parâmetros possam ser totalmente conhecidos antes do início do bloco. Você pode querer ler a documentação para obter mais informações.

2) Por que precisamos iterar e esperar até que ninguém altere o valor antes do início da transação?

Este é um exemplo de padrão otimista simultâneo.

Se não usássemos WATCH/MULTI/EXEC, teríamos uma possível condição de corrida:
# Initial arbitrary value
powerlevel = 10
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: SET powerlevel 11
session B: SET powerlevel 11
# In the end we have 11 instead of 12 -> wrong

Agora vamos adicionar um bloco WATCH/MULTI/EXEC. Com uma cláusula WATCH, os comandos entre MULTI e EXEC são executados somente se o valor não for alterado.
# Initial arbitrary value
powerlevel = 10
session A: WATCH powerlevel
session B: WATCH powerlevel
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: MULTI
session B: MULTI
session A: SET powerlevel 11 -> QUEUED
session B: SET powerlevel 11 -> QUEUED
session A: EXEC -> success! powerlevel is now 11
session B: EXEC -> failure, because powerlevel has changed and was watched
# In the end, we have 11, and session B knows it has to attempt the transaction again
# Hopefully, it will work fine this time.

Portanto, você não precisa iterar para esperar até que ninguém altere o valor, mas tentar a operação repetidamente até que o Redis tenha certeza de que os valores são consistentes e sinalize que foi bem-sucedido.

Na maioria dos casos, se as "transações" forem rápidas o suficiente e a probabilidade de haver contenção for baixa, as atualizações serão muito eficientes. Agora, se houver contenção, algumas operações extras terão que ser feitas para algumas "transações" (devido à iteração e novas tentativas). Mas os dados sempre serão consistentes e nenhum bloqueio é necessário.