Cada comando en Redis es atómico, se ejecuta de forma completa e independiente, o en caso de fallo, no aplicarán ningún cambio a la base de datos, para mantener su integridad. Sin embargo, en ocasiones podemos necesitar el uso de Transacciones en Redis, poder ejecutar varios comandos de forma atómica, como si fuera uno solo, y que se ejecuten todos ellos o en caso de fallo en cualquiera de ellos, no se ejecute ninguno, para así poder garantizar la integridad de nuestra información, al igual que necesitaremos una gestión optimista de bloqueos.
Continuando con nuestra serie sobre Redis, ahora que ya hemos hablado sobre las diferentes estructuras de datos que soporta Redis (Strings, Hashes, Lists, Sets, y Sorted Sets), y del problema y posibles soluciones de la búsqueda sobre claves en función de sus Atributos, llega el momento de hablar sobre las Transacciones en Redis y la gestión optimista de bloqueos.
Introducción a las Transacciones en Redis
Una Transacción en Redis permite encolar un conjunto de comandos para su posterior ejecución secuencial de forma atómica como un lote (batch), de tal modo, que se ejecuten todos, o en caso de fallo en cualquiera de ellos, no se aplique ningún cambio a la base de datos (no se ejecute ninguno), para garantizar la integridad de nuestra información.
No es posible crear Transacciones anidadas en Redis, y tampoco existe la posibilidad de ejecutar un Rollback (si una transacción incluye un comando con un error de sintaxis, la Transacción completa será descartada, en cualquier otro caso, será ejecutada). Esto permite a Redis que pueda ofrecer un máximo rendimiento y mínima latencia.
Como vemos, las Transacciones en Redis no son exactamente igual a como lo son en las bases de datos relacionales. En una base de datos relacional se suelen ejecutar los comando de las transacciones de forma inmediata pudiendo anidar transacciones y necesitan la posibilidad de tener un mecanismo de Rollback, mientras que en Redis los comandos son encolados para su posterior ejecución, lo que permite que Redis pueda funcionar sin necesidad de un mecanismo de Rollbacks y no se permiten las transacciones anidadas (resultando en un modelo más sencillo, y favorece un mayor rendimiento).
Para trabajar con Transacciones en Redis, tenemos disponibles varios comandos.
- Utilizaremos el comando MULTI para comenzar una Transacción, seguido de los diferentes comandos que deseamos ejecutar, secuencialmente en el orden que deseamos. Los posteriores comandos, no serán ejecutados, sino encolados para su posterior ejecución en orden, uno a uno.
- Utilizaremos el comando EXEC para indicar el final de la Transacción y comenzar a ejecutar secuencialmente sus comandos. Importante, comprender que antes de la ejecución de EXEC, no se ha ejecutado ningún comando, simplemente se han añadido a una cola, en espera del comando EXEC para iniciar su ejecución atómica en un único lote.
- Utilizaremos el comando DISCARD para abortar o cancelar una Transacción que hemos comenzado previamente con MULTI y que aún no hemos ejecutado con EXEC, descartando todos los comandos que fueron encolados.
A continuación se muestra el ejemplo de una Transacción en Redis, con los comandos MULTI y EXEC.
MULTI
DECR sales:flight-3816:available-seats
GET sales:flight-3816:available-seats
EXEC
El siguiente ejemplo muestra un Transacción que será abortada por un error de sintaxis (el comando FORCERROR no existe). Todos los comandos son encolados, para al ejecutar el comando EXEC, se detecta el error de sintaxis y se aborta la ejecución de todos los comandos de la Transacción.
MULTI
DECR sales:flight-3816:available-seats
FORCERROR
GET sales:flight-3816:available-seats
EXEC
El siguiente ejemplo muestra una Transacción abortada, con los comandos MULTI y DISCARD.
MULTI
DECR sales:flight-3816:available-seats
GET sales:flight-3816:available-seats
DISCARD
Control optimista de la concurrencia (Optimistic Locking)
Todo lo que hemos visto hasta ahora sobre las Transacciones en Redis está muy bien, pero la realidad es que resulta insuficiente: si obtengo un valor de Redis, y luego quiero ejecutar una transacción para modificarlo, cómo lo podría hacer? O dicho de otro modo, cómo puedo ejecutar una Transacción en Redis en base al valor de una o varias claves que he obtenido previamente, con garantía de que no han sido alteradas previamente a la ejecución de mi Transacción?
Esto también lo podemos conseguir en Redis, mediante su sistema de notificaciones del espacio de claves (keyspace notifications), y los comandos WATCH y UNWATCH.
- El comando WATCH debe ejecutarse antes del comienzo de una Transacción (antes del comando MULTI), y permite observar una o varias claves, de tal modo que al ejecutar el comando EXEC, la Transacción fallará si las variables observadas han cambiado desde entonces. Es posible incluso ejecutar varios comandos WATCH (su efecto es acumulativo).
- El comando UNWATCH permite dejar de observar las variables que se estaban observando previamente. Hay que tener en cuenta, que después de ejecutar un comando EXEC, se deja automáticamente de observar todas las claves.
A continuación se muestra un ejemplo.
WATCH sales:flight-3816:available-seats
GET sales:flight-3816:available-seats
MULTI
DECR sales:flight-3816:available-seats
EXEC
GET sales:flight-3816:available-seats
Despedida y Cierre
Hasta aquí llega este Post, en el que hemos hablado de las Transacciones en Redis, cómo funcionan y la diferencia con la gestión de transacciones en las bases de datos relacionales, los comandos habituales para trabajar con Transacciones (MULTI, EXEC, DISCARD, WATCH, UNWATCH), y la gestión optimista de bloqueos.
Poco más por hoy. Como siempre, confío que la lectura resulte de interés.