Todos los productos
Search
Centro de documentos

Tair (Redis® OSS-Compatible):Implementación de bloqueos distribuidos de alto rendimiento con TairString

Última actualización: Sep 03, 2026

Cuando varios hosts compiten por un recurso compartido, se requieren bloqueos distribuidos para evitar la corrupción de datos y los fallos lógicos. Este tema explica el funcionamiento de los bloqueos distribuidos, cómo implementarlos en ApsaraDB for Redis y Tair (Enterprise Edition), y cómo mantener la consistencia del bloqueo tras un failover. Los comandos CAD y CAS específicos de Tair eliminan la necesidad de scripts de Lua requeridos por Redis estándar, lo que reduce la complejidad de implementación y mejora el rendimiento.

Cuándo usar bloqueos distribuidos

Los distintos escenarios de concurrencia requieren diferentes mecanismos de coordinación:

Escenario

Mecanismo

Varios hilos en el mismo proceso

Mutex o bloqueo de lectura/escritura

Varios procesos en el mismo host

Semáforo, pipeline o memoria compartida

Varios hosts en un sistema distribuido

Bloqueo distribuido

Los bloqueos distribuidos son bloqueos de exclusión mutua con alcance global. Utilícelos para coordinar el acceso a recursos compartidos entre hosts y prevenir fallos lógicos causados por la contención de recursos.

Propiedades de un bloqueo distribuido bien implementado

Un bloqueo distribuido debe cumplir tres propiedades. Cada enfoque de implementación en este tema se evalúa con base en estas propiedades:

  • Exclusión mutua: En cualquier momento dado, solo un cliente posee el bloqueo.

  • Libre de interbloqueos: Los bloqueos utilizan un mecanismo basado en concesiones. Si un cliente encuentra una excepción después de adquirir un bloqueo, este expira automáticamente y evita el bloqueo indefinido de recursos.

  • Consistente: Después de un cambio por alta disponibilidad (HA), el estado del bloqueo debe permanecer intacto. Los cambios pueden desencadenarse por errores externos (fallos de hardware, excepciones de red) o errores internos (consultas lentas, defectos del sistema). Cuando ocurre un cambio, un nodo réplica se promueve como el nuevo nodo maestro.

Implementación de bloqueos distribuidos en Redis

Los métodos de esta sección aplican tanto a ApsaraDB for Redis como a Redis Open-Source Edition.

Adquisición de un bloqueo

Ejecute el comando SET con las opciones NX y EX para adquirir un bloqueo de forma atómica:

SET resource_1 random_value NX EX 5

Parámetro

Descripción

resource_1

La clave del bloqueo. Si esta clave existe, el recurso está bloqueado e inaccesible para otros clientes.

random_value

Una cadena aleatoria única para todos los clientes. Se usa para verificar la propiedad antes de liberar el bloqueo.

EX

El periodo de validez del bloqueo en segundos. Use PX para precisión en milisegundos.

NX

Cancela la operación SET si la clave ya existe.

En este ejemplo, el bloqueo expira después de 5 segundos si no se libera explícitamente. La expiración previene interbloqueos: si el cliente falla, el sistema recupera el bloqueo automáticamente.

Liberación de un bloqueo

Ejecutar DEL resource_1 directamente no es seguro. Considere este escenario de fallo:

  1. En t1, la aplicación 1 adquiere resource_1 con una concesión de 3 segundos.

  2. La aplicación 1 se detiene durante más de 3 segundos. La clave expira en t2 y el bloqueo se libera automáticamente.

  3. En t3, la aplicación 2 adquiere el bloqueo.

  4. La aplicación 1 se reanuda en t4 y ejecuta DEL resource_1, lo que libera accidentalmente un bloqueo que ya no posee.

Un bloqueo solo debe ser liberado por el cliente que lo estableció. Use el siguiente script de Lua para verificar la propiedad y eliminar de forma atómica:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

Renovación de un bloqueo

Si el cliente no puede completar su trabajo dentro del periodo de concesión, debe renovar el bloqueo. Solo el cliente que estableció el bloqueo puede renovarlo:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("expire", KEYS[1], ARGV[2])
else
    return 0
end

Implementación de bloqueos distribuidos en Tair

Instancias compatibles: Instancias basadas en DRAM de Tair e instancias optimizadas para memoria persistente.

En estas instancias, el tipo de dato TairString proporciona los comandos CAD y CAS. Estos comandos reemplazan los scripts de Lua anteriores con operaciones atómicas únicas, lo que reduce la complejidad de implementación y mejora el rendimiento.

Adquisición de un bloqueo

La adquisición del bloqueo es idéntica al enfoque de Redis:

SET resource_1 random_value NX EX 5

Liberación de un bloqueo

El comando CAD (Compare-And-Delete) verifica la propiedad y elimina la clave en una sola operación atómica, lo que reemplaza por completo el script de Lua:

CAD resource_1 my_random_value

Esto equivale a: si GET(resource_1) == my_random_value, ejecute DEL(resource_1).

Renovación de un bloqueo

El comando CAS (Compare-And-Swap) extiende la expiración del bloqueo en una sola operación atómica, lo que reemplaza por completo el script de Lua:

CAS resource_1 my_random_value my_random_value EX 10
El comando CAS no verifica si el nuevo valor es igual al valor original. Pase el mismo valor aleatorio tanto para el campo de valor antiguo como para el nuevo a fin de preservar la identidad del bloqueo mientras extiende la concesión.

Código de ejemplo con Jedis

Los siguientes ejemplos en Java muestran un ciclo de vida completo de bloqueo mediante Jedis. Las tres operaciones (adquisición, liberación y renovación) usan la misma resourceKey y el mismo randomValue.

Definición de los comandos CAS y CAD

enum TairCommand implements ProtocolCommand {
    CAD("CAD"), CAS("CAS");

    private final byte[] raw;

    TairCommand(String alt) {
        raw = SafeEncoder.encode(alt);
    }

    @Override
    public byte[] getRaw() {
        return raw;
    }
}

Adquisición de un bloqueo

public boolean acquireDistributedLock(Jedis jedis, String resourceKey, String randomValue, int expireTime) {
    SetParams setParams = new SetParams();
    setParams.nx().ex(expireTime);
    String result = jedis.set(resourceKey, randomValue, setParams);
    return "OK".equals(result);
}

Liberación de un bloqueo

public boolean releaseDistributedLock(Jedis jedis, String resourceKey, String randomValue) {
    jedis.getClient().sendCommand(TairCommand.CAD, resourceKey, randomValue);
    Long ret = jedis.getClient().getIntegerReply();
    return 1 == ret;
}

Renovación de un bloqueo

public boolean renewDistributedLock(Jedis jedis, String resourceKey, String randomValue, int expireTime) {
    jedis.getClient().sendCommand(TairCommand.CAS, resourceKey, randomValue, randomValue, "EX", String.valueOf(expireTime));
    Long ret = jedis.getClient().getIntegerReply();
    return 1 == ret;
}

Garantía de la consistencia del bloqueo tras un failover

La replicación de maestro a réplica es asíncrona. Si un nodo maestro falla después de escribir un bloqueo pero antes de que se replique la escritura, la réplica promovida no tendrá registro de dicho bloqueo. Dos clientes podrían entonces poseer el mismo bloqueo simultáneamente, lo que viola la propiedad de consistencia.

Tres enfoques abordan este riesgo:

Enfoque

Costo

Limitaciones

Ideal para

Algoritmo Redlock

Alto (requiere varias instancias)

No se puede usar con instancias de clúster o instancias maestro-réplica estándar; adquisición de bloqueo más lenta

Requisitos máximos de tolerancia a fallos

Comando WAIT

Bajo

No garantiza la consistencia si ocurre un cambio antes de que WAIT retorne

Despliegues estándar donde el costo es relevante

Replicación semisíncrona de Tair

Integrado

Se degrada a asíncrono si falla una réplica

Cargas de trabajo de alta concurrencia en instancias Tair

Uso del algoritmo Redlock

El algoritmo Redlock, propuesto por los fundadores del proyecto de código abierto Redis, reduce la probabilidad de pérdida de bloqueo al distribuir los bloqueos en N instancias independientes de Redis. Una sola instancia maestro-réplica puede perder un bloqueo con una probabilidad de k% durante un cambio. Con Redlock, la probabilidad de que las N instancias pierdan sus bloqueos simultáneamente cae a (k%)^N.

El algoritmo requiere que los bloqueos tengan éxito en M de N nodos, donde 1 < M ≤ N. No es necesario que los N bloqueos tengan éxito simultáneamente.

Compromisos de Redlock:

  • La adquisición y liberación de bloqueos son más lentas porque intervienen varias instancias.

  • Requiere varias instancias independientes de ApsaraDB for Redis o de Redis autogestionado, lo que incrementa los costos de infraestructura.

  • Es incompatible con instancias de clúster e instancias maestro-réplica estándar.

Uso del comando WAIT

El comando WAIT bloquea al cliente actual hasta que todos los comandos de escritura anteriores se repliquen en un número especificado de nodos réplica, o hasta que expire el tiempo de espera (en milisegundos). El comando WAIT es mucho más rentable que el algoritmo Redlock.

Después de adquirir un bloqueo, ejecute WAIT para confirmar la replicación:

SET resource_1 random_value NX EX 5
WAIT 1 5000

En este ejemplo, el periodo de tiempo de espera es de 5,000 milisegundos. Si WAIT retorna 1, los datos están sincronizados entre el nodo maestro y los nodos réplica, y se garantiza la consistencia. Sin embargo, si se activa un cambio por HA antes de que WAIT retorne una respuesta exitosa, los datos podrían perderse; el valor de retorno solo indica un posible fallo de sincronización. Después de que WAIT retorne un error, vuelva a adquirir el bloqueo o verifique su estado antes de continuar.

Notas de uso:

  • WAIT solo bloquea al cliente que lo envía; los demás clientes no se ven afectados.

  • No ejecute WAIT antes de liberar un bloqueo. Los bloqueos distribuidos son exclusivos por diseño, por lo que una liberación ligeramente retrasada no causa fallos lógicos.

Uso de Tair

Las instancias basadas en DRAM de Tair abordan la consistencia de los bloqueos a nivel de infraestructura:

  • Rendimiento 3x: Las instancias basadas en DRAM de Tair ofrecen tres veces el rendimiento de Redis de código abierto, y mantienen cargas de trabajo de bloqueo de alta concurrencia sin interrupción del servicio.

  • Replicación semisíncrona: Se retorna una respuesta exitosa solo después de que la escritura se confirma en el nodo maestro y se replica en como mínimo un nodo réplica. Esto evita la pérdida de bloqueos tras un cambio.

La replicación semisíncrona se degrada a replicación asíncrona si falla un nodo réplica o si ocurre una excepción de red durante la sincronización.

Los comandos CAD y CAS reducen aún más la complejidad de implementación en comparación con los scripts de Lua y eliminan la necesidad de múltiples instancias independientes que requiere Redlock.