Mecanismos Transaccionales en Redis para Garantizar Consistencia

Comandos Fundamentales para Transacciones

Inicio de Bloque Transaccional: MULTI

El comando MULTI inicia un bloque transaccional, encolando operacoines subsiguientes sin ejecutarlas inmediatamente:

MULTI
SET producto:existencia 50
SET pedido:usuario123 estado "pendiente"

Ejecución Transaccional: EXEC

EXEC ejecuta todas las operaciones encoladas atómicamente:

EXEC

Cancelación de Transacciones: DISCARD

Descarta todas las operaciones pendientes en el bloque actual:

MULTI
SET item:A 100
SET item:B 200
DISCARD

Monitorización de Claves: WATCH

Bloquea la ejecución si las claves vigiladas son modificadas externamente:

WATCH saldo:cliente
MULTI
SET saldo:cliente 500
EXEC

Escnearios Prácticos de Aplicación

Sistema de Inventarios

Actualización atómica de existencias y registro de ventas:

WATCH inventario:zapatos
MULTI
DECR inventario:zapatos
HSET venta:dia123 producto "zapatos" cantidad 1
EXEC

Transferencias Bancarias

Movimientos monetarios atómicos entre cuentas:

WATCH cuenta:origen cuenta:destino
MULTI
DECRBY cuenta:origen 300
INCRBY cuenta:destino 300
EXEC

Consideraciones Clave

Manejo de Errores

Los errores sintácticos solo se detectan durante EXEC, deteniendo toda la transacción.

Ausencia de Rollback Automático

Comandos fallidos no revierten operaciones exitosas previas en la misma transacción.

Impacto en Rendimiento

Ejecución secuencial en bloques grandes puede generar cuellos de botella.

Implementaciones en Go

Ejemplo de Sistema de Inventarios

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    cliente := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    err := cliente.Watch(ctx, func(tx *redis.Tx) error {
        if tx.Get(ctx, "inventario:zapatos").Err() != nil {
            return err
        }
        operaciones := tx.TxPipeline()
        operaciones.Decr(ctx, "inventario:zapatos")
        operaciones.HSet(ctx, "venta:dia123", "producto", "zapatos", "cantidad", 1)
        _, err := operaciones.Exec(ctx)
        return err
    }, "inventario:zapatos")

    if err != nil {
        fmt.Println("Error en transacción:", err)
    }
}

Ejemplo de Transferencia Bancaria

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    conexion := redis.NewClient(&redis.Options{
        Addr: "servidor:6379",
    })

    err := conexion.Watch(ctx, func(tx *redis.Tx) error {
        if tx.Get(ctx, "cuenta:origen").Err() != nil || tx.Get(ctx, "cuenta:destino").Err() != nil {
            return err
        }
        pipeline := tx.TxPipeline()
        pipeline.DecrBy(ctx, "cuenta:origen", 300)
        pipeline.IncrBy(ctx, "cuenta:destino", 300)
        _, err := pipeline.Exec(ctx)
        return err
    }, "cuenta:origen", "cuenta:destino")

    if err != nil {
        fmt.Println("Fallo en transferencia:", err)
    }
}

Etiquetas: Redis transacciones consistencia go-redis Go

Publicado el 6-22 17:51