sql >> Base de Datos >  >> NoSQL >> Redis

Transacciones y estado de cuenta en Redis

Aquí hay varias preguntas.

1) ¿Por qué no podemos ejecutar incrementos en transacciones que no pueden ser interrumpidas por otro comando?

Tenga en cuenta primero que las "transacciones" de Redis son completamente diferentes de lo que la mayoría de la gente piensa que son las transacciones en DBMS clásico.

# Does not work
redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec()

Debe comprender qué se ejecuta en el lado del servidor (en Redis) y qué se ejecuta en el lado del cliente (en su secuencia de comandos). En el código anterior, los comandos GET y SET se ejecutarán en el lado de Redis, pero se supone que la asignación a la corriente y el cálculo de la corriente + 1 se ejecutarán en el lado del cliente.

Para garantizar la atomicidad, un bloque MULTI/EXEC retrasa la ejecución de los comandos de Redis hasta el exec. Entonces, el cliente solo acumulará los comandos GET y SET en la memoria, y los ejecutará de una sola vez y atómicamente al final. Por supuesto, el intento de asignar corriente al resultado de GET y el incremento ocurrirá mucho antes. En realidad, el método redis.get solo devolverá la cadena "EN COLA" para indicar que el comando se retrasó y el incremento no funcionará.

En los bloques MULTI/EXEC solo puede utilizar comandos cuyos parámetros se puedan conocer por completo antes del comienzo del bloque. Es posible que desee leer la documentación para obtener más información.

2) ¿Por qué necesitamos iterar y esperar hasta que nadie cambie el valor antes de que comience la transacción?

Este es un ejemplo de patrón optimista concurrente.

Si no usáramos WATCH/MULTI/EXEC, tendríamos una posible condición de carrera:

# 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

Ahora agreguemos un bloque WATCH/MULTI/EXEC. Con una cláusula WATCH, los comandos entre MULTI y EXEC se ejecutan solo si el valor no ha cambiado.

# 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.

Por lo tanto, no tiene que iterar para esperar hasta que nadie cambie el valor, sino intentar la operación una y otra vez hasta que Redis esté seguro de que los valores son consistentes y señale que se realizó correctamente.

En la mayoría de los casos, si las "transacciones" son lo suficientemente rápidas y la probabilidad de que haya contención es baja, las actualizaciones son muy eficientes. Ahora, si hay contención, se deberán realizar algunas operaciones adicionales para algunas "transacciones" (debido a la iteración y los reintentos). Pero los datos siempre serán consistentes y no se requiere bloqueo.