tutorial labs aws redis

labs - redis vs mongodb



Transacciones y declaraciĆ³n de reloj en Redis (1)

¿Podría por favor explicarme el siguiente ejemplo de "The Little Redis Book":

Con el código anterior, no podríamos implementar nuestro propio comando incr ya que todos se ejecutan juntos una vez que se llama a exec. Desde el código, no podemos hacer:

redis.multi() current = redis.get(''powerlevel'') redis.set(''powerlevel'', current + 1) redis.exec()

Así no es como funcionan las transacciones Redis. Pero, si agregamos un reloj a powerlevel, podemos hacer:

redis.watch(''powerlevel'') current = redis.get(''powerlevel'') redis.multi() redis.set(''powerlevel'', current + 1) redis.exec()

Si otro cliente cambia el valor de powerlevel después de que hayamos llamado a verlo, nuestra transacción fallará. Si ningún cliente cambia el valor, el conjunto funcionará. Podemos ejecutar este código en un bucle hasta que funcione.

¿Por qué no podemos ejecutar incrementos en transacciones que no pueden ser interrumpidas por otro comando? ¿Por qué necesitamos iterar y esperar hasta que nadie cambie de valor antes de que comience la transacción?


Hay varias preguntas aquí.

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

En primer lugar, tenga en cuenta que las "transacciones" de Redis son completamente diferentes de lo que la mayoría de la gente piensa que las transacciones son en el 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 script). En el código anterior, los comandos GET y SET se ejecutarán en el lado Redis, pero se supone que la asignación a la corriente y el cálculo de la corriente + 1 se ejecutan en el lado del cliente.

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

En los bloques MULTI / EXEC, solo puede usar comandos cuyos parámetros se puedan conocer completamente 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 de 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 más bien para intentar la operación una y otra vez hasta que Redis esté seguro de que los valores son coherentes y señala que es exitoso.

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, habrá que 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.