Bloqueo distribuido por Redis (implementación PHP)

Redis implementa bloqueos distribuidos

Escenarios de aplicación (por ejemplo, resolver el problema de sobreventa de inventario)

¿Cómo ocurrió la sobreventa de inventario?

Primero echemos un vistazo a lo que significa el llamado inventario de comercio electrónico sobrevendido si no utiliza bloqueos distribuidos. Mire la siguiente imagen:
Inserte la descripción de la imagen aquí
Esta imagen es realmente muy clara, asumiendo que el sistema de pedidos se implementa en dos máquinas, diferentes usuarios tienen que comprar 10 iPhones al mismo tiempo y envían una solicitud al sistema de pedidos.

Luego, cada instancia del sistema de pedidos se verificó en la base de datos y el inventario actual de iPhone es de 12 unidades.

Los dos hermanos mayores los miraron y se alegraron ¡El inventario de 12 unidades es mayor que el número de 10 unidades para comprar!

Desde entonces, cada instancia del sistema de pedidos envía SQL a la base de datos para realizar un pedido y luego deduce 10 inventarios, uno de los cuales deduce el inventario de 12 a 2 y el otro deduce el inventario de 2 a -8. estación.

Ahora se acabó, ¡el inventario tiene un número negativo! ¡Las lágrimas corren, no se han enviado 20 iPhones a dos usuarios! ¿Cómo puede ser bueno esto?

¿Cómo solucionar el problema de sobreventa de inventario con cerraduras distribuidas?

Para una llave de candado, solo un cliente puede obtener el candado al mismo tiempo, y otros clientes esperarán infinitamente para intentar obtener ese candado, solo el cliente que ha obtenido el candado puede ejecutar la siguiente lógica de negocios.
Inserte la descripción de la imagen aquí
Puede seguir el número de paso anterior y volver a leerlo, y lo entenderá de inmediato.

Como puede ver en la figura anterior, solo una instancia del sistema de pedidos puede agregar correctamente un bloqueo distribuido, y luego solo una instancia puede verificar el inventario, determinar si el inventario es suficiente, realizar un pedido para deducir el inventario y luego liberar el bloqueo.

Una vez que se libera el bloqueo, se puede bloquear otra instancia del sistema de pedidos y luego verificar el inventario.Se encuentra que solo hay 2 unidades en el inventario y el inventario es insuficiente para comprar y el pedido falla. No deducirá el inventario a -8.

¿Existe alguna otra solución para resolver el problema del inventario sobrevendido?

¡Por supuesto que la hay! Por ejemplo, bloqueos pesimistas, bloqueos distribuidos, bloqueos optimistas, serialización de colas, descentralización de colas asíncronas, operaciones atómicas de Redis, etc., para muchas soluciones, tenemos nuestro propio conjunto de mecanismos de optimización para la sobreventa de inventario.

Pero como se mencionó anteriormente, este artículo hablará sobre la optimización concurrente de bloqueos distribuidos, no sobre la solución de la sobreventa de inventario, por lo que la sobreventa de inventario es solo un escenario comercial.

En el futuro, tengo la oportunidad de escribir un artículo sobre la solución al problema de la sobreventa de inventario de comercio electrónico. Este artículo se enfoca primero en la optimización de una concurrencia de bloqueo distribuido. Espero que todos entiendan la intención y los antecedentes para evitar que algunos hermanos no lo lean. Limpiar y vomitar.

Y sugiero que incluso si tiene objeciones al contenido del artículo, deje un mensaje en el backstage de la cuenta oficial para discutir conmigo La tecnología es para comunicar más, abrir ideas y chocar con el pensamiento.

Qué condiciones debe tener un candado distribuido:

1. En un entorno de sistema distribuido, un método solo puede ser ejecutado por un subproceso de una máquina al mismo tiempo;
2. Adquisición y liberación de bloqueos de alta disponibilidad;
3. Adquisición y liberación de bloqueos de alto rendimiento;
4. Disponible Función de reingreso,
5. Con mecanismo de falla de bloqueo para evitar interbloqueo,
6. Con característica de bloqueo sin bloqueo, es decir, si no se adquiere el bloqueo, volverá directamente a la falla de adquisición del bloqueo.

PHP implementa el bloqueo distribuido de Redis

class RedisMutexLock
{
    
    
    /**
     * 缓存 Redis 连接。
     *
     * @return void
     */
    public static function getRedis()
    {
    
    
        // 这行代码请根据自己项目替换为自己的获取 Redis 连接。
        return YCache::getRedisClient();
    }

    /**
     * 获得锁,如果锁被占用,阻塞,直到获得锁或者超时。
     * -- 1、如果 $timeout 参数为 0,则立即返回锁。
     * -- 2、建议 timeout 设置为 0,避免 redis 因为阻塞导致性能下降。请根据实际需求进行设置。
     *
     * @param  string  $key         缓存KEY。
     * @param  int     $timeout     取锁超时时间。单位(秒)。等于0,如果当前锁被占用,则立即返回失败。如果大于0,则反复尝试获取锁直到达到该超时时间。
     * @param  int     $lockSecond  锁定时间。单位(秒)。
     * @param  int     $sleep       取锁间隔时间。单位(微秒)。当锁为占用状态时。每隔多久尝试去取锁。默认 0.1 秒一次取锁。
     * @return bool 成功:true、失败:false
     */
    public static function lock($key, $timeout = 0, $lockSecond = 20, $sleep = 100000)
    {
    
    
        if (strlen($key) === 0) {
    
    
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        if (!is_int($timeout) || $timeout < 0) {
    
    
            YCore::exception(500, "timeout 参数设置有误");
        }
        $start = self::getMicroTime();
        $redis = self::getRedis();
        do {
    
    
            // [1] 锁的 KEY 不存在时设置其值并把过期时间设置为指定的时间。锁的值并不重要。重要的是利用 Redis 的特性。
            $acquired = $redis->set("Lock:{
      
      $key}", 1, ['NX', 'EX' => $lockSecond]);
            if ($acquired) {
    
    
                break;
            }
            if ($timeout === 0) {
    
    
                break;
            }
            usleep($sleep);
        } while ((self::getMicroTime()) < ($start + ($timeout * 1000000)));
        return $acquired ? true : false;
    }

    /**
     * 释放锁
     *
     * @param  mixed  $key  被加锁的KEY。
     * @return void
     */
    public static function release($key)
    {
    
    
        if (strlen($key) === 0) {
    
    
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        $redis = self::getRedis();
        $redis->del("Lock:{
      
      $key}");
    }

    /**
     * 获取当前微秒。
     *
     * @return bigint
     */
    protected static function getMicroTime()
    {
    
    
        return bcmul(microtime(true), 1000000);
    }
}

El contenido se organiza haciendo referencia a artículos en línea, solo para uso personal de aprendizaje:

  1. https://zhuanlan.zhihu.com/p/95217001
  2. https://www.cnblogs.com/liuqingzheng/p/11080501.html
  3. https://blog.csdn.net/C18298182575/article/details/92786579

Supongo que te gusta

Origin blog.csdn.net/magentodaddy/article/details/108538672
Recomendado
Clasificación