Todos los productos
Search
Centro de documentos

Tair (Redis® OSS-Compatible):Implementación de bloqueo optimista de alto rendimiento mediante TairString

Última actualización: Sep 03, 2026

Cuando varios clientes leen y actualizan simultáneamente la misma clave, la última escritura sobrescribe silenciosamente todas las anteriores. Esta condición de carrera se conoce como "last-write-wins". TairString, el tipo de datos de cadena extendida (exString) en Tair (Enterprise Edition), incluye un número de versión integrado que convierte el bloqueo optimista en una operación nativa. Esto permite detectar y reintentar escrituras conflictivas sin necesidad de bloqueos externos ni transacciones.

Funcionamiento del bloqueo optimista basado en números de versión

El bloqueo optimista asigna un número de versión a cada clave. Al leer una clave, también obtiene su versión. Al escribir, proporciona la versión leída previamente. Si otro cliente escribió en la clave durante ese intervalo, la versión del servidor se incrementó y ya no coincide con la suya; por lo tanto, la escritura se rechaza en lugar de aplicarse silenciosamente. Posteriormente, reintente la operación con el valor y la versión actualizados.

TairString resuelve este problema de forma nativa: las cadenas nativas de Redis solo constan de claves y valores, sin ningún mecanismo para detectar si una clave cambió entre una lectura y una escritura.

La condición de carrera: por qué last-write-wins provoca pérdida de datos

A continuación se muestra un diagrama que ilustra una condición de carrera típica.

Concurrent write race condition

Considere un contador que dos clientes incrementan simultáneamente. Ambos leen el valor actual (10), calculan independientemente 11 y escriben el resultado. El resultado final es 11 en lugar del 12 esperado, por lo que uno de los incrementos se pierde silenciosamente. Este mismo patrón aplica a cualquier clave compartida:

  1. key_1 comienza con el valor hello.

  2. La aplicación 1 lee hello.

  3. La aplicación 2 lee hello.

  4. La aplicación 1 escribe world en key_1.

  5. La aplicación 2 escribe universe en key_1.

La aplicación 1 espera que key_1 sea world, pero el valor real es universe. Para evitar esto, la secuencia de lectura-modificación-escritura debe ser atómica, función que proporciona exactamente el mecanismo de números de versión de TairString.

Comandos de TairString para bloqueo optimista

Los TairStrings constan de claves, valores y números de versión. TairString integra todas las funciones de cadenas de Redis, excepto las operaciones de bits.

Nota

Los comandos de TairString y los comandos de cadenas nativas de Redis no son intercambiables.

Comando

Propósito

Comportamiento

EXSET

Cree una clave TairString

El número de versión predeterminado es 1

EXGET

Consultar una clave TairString

Devuelve tanto value como version

EXSET con versión

Actualice con verificación de versión

Se ejecuta correctamente e incrementa la versión en 1; falla con ERR update version is stale si la versión no coincide

EXCAS

Compare-And-Swap en una sola llamada

Verifica la versión y aplica la escritura atómicamente; en caso de fallo, devuelve el error, el valor actual y la versión actual en una sola respuesta

Para consultar la referencia completa de comandos, consulte exString.

Implementación de bloqueo optimista con EXGET y EXSET

El patrón básico de bloqueo optimista utiliza un bucle de reintento: lea el valor y la versión actuales con EXGET, calcule el nuevo valor y luego escriba con EXSET al pasar la versión leída. Si ocurrió una escritura competitiva entre su lectura y su escritura, la verificación de versión falla y el bucle se reintenta.

while(true){
    {value, version} = EXGET(key);      // Retrieve the value and version number of the key.
    value2 = update(...);               // Save the new value as value 2.
    ret = EXSET(key, value2, version);  // Update the key and assign the return value to the ret variable.
    if(ret == OK)
       break;                           // If the return value is OK, the update is successful and the while loop exits.
    else if (ret.contains("version is stale"))
       continue;                        // If the return value contains the "version is stale" error message, the update fails and the while loop is repeated.
}

Esto garantiza que, si algún otro cliente actualizó la clave después de su EXGET, su EXSET fallará en lugar de sobrescribir silenciosamente dicho cambio. Cada iteración de este bucle requiere dos viajes de ida y vuelta a Tair: un EXGET para leer el valor y la versión actuales, y un EXSET para intentar la escritura.

Nota
  • Cuando se elimina una clave TairString y se vuelve a crear con el mismo nombre, la nueva clave comienza en la versión 1. No hereda la versión anterior.

  • Para omitir la verificación de versión y forzar la sobreescritura de un valor, utilice la opción ABS con EXSET. Consulte EXSET para obtener más detalles.

Reducción de E/S con EXCAS

El bucle EXGET seguido de EXSET requiere dos operaciones de E/S por ciclo de reintento. El comando EXCAS (Compare-And-Swap) reduce esto a una sola operación de E/S por ciclo, lo que representa una mejora significativa bajo alta concurrencia.

EXCAS verifica atómicamente la versión y aplica la escritura en un solo viaje de ida y vuelta. En caso de fallo, devuelve a continuación tres elementos en una sola respuesta:

  • "ERR update version is stale"

  • El valor actual

  • El número de versión actual

Dado que el estado actual se incluye en la respuesta de fallo, el siguiente ciclo de reintento puede comenzar inmediatamente sin necesidad de una llamada adicional a EXGET.

while(true){
    {ret, value, version} = excas(key, new_value, old_version)    // Use the CAS command to replace the original value with a new value.
    if(ret == OK)
       break;                                                     // If the return value is OK, the update is successful and the while loop exits.
    else (if ret.contains("update version is stale"))            // If the return value contains the "update version is stale" error message, the update fails. The values of the value and old_version variables are updated.
       update(value);
       old_version = version;
 }

Temas relacionados