实现分布式锁是在分布式环境中确保资源独占性的重要手段。在这里,我将使用 PHP 和 Redis 来展示如何实现基于 Redis 的分布式锁。

首先,我们需要使用 Redis 的 SET 指令来尝试获取锁,并设置一个过期时间,确保锁不会永久存在。在 Redis 中,我们可以利用 SET 指令的特性,例如在给定的键不存在时设置键的值并设置过期时间来实现分布式锁。

下面是一个使用 PHP 和 Redis 实现分布式锁的示例:

<?php

class RedisLock
{
    private $redis;
    private $lockKey;
    private $timeout; // 锁的超时时间,单位:秒

    public function __construct($redis, $lockKey, $timeout = 10)
    {
        $this->redis = $redis;
        $this->lockKey = $lockKey;
        $this->timeout = $timeout;
    }

    public function acquireLock()
    {
        $startTime = time();
        $expiry = $startTime + $this->timeout + 1; // 锁的超时时间加一,确保不会因为客户端时间偏差导致误判

        while (time() < $expiry) {
            $result = $this->redis->set($this->lockKey, 1, ['NX', 'EX' => $this->timeout]);
            if ($result === true) {
                return true; // 获取锁成功
            }

            usleep(10000); // 等待10毫秒后重试
        }

        return false; // 获取锁超时
    }

    public function releaseLock()
    {
        $this->redis->del($this->lockKey);
    }
}

// 使用示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$lockKey = 'my_distributed_lock';
$lock = new RedisLock($redis, $lockKey);

if ($lock->acquireLock()) {
    try {
        // 在获取到锁之后,执行需要加锁的代码块
        echo "Lock acquired! Performing critical section...\n";
        sleep(5); // 模拟需要加锁保护的代码

        echo "Critical section complete.\n";
    } finally {
        $lock->releaseLock(); // 释放锁
    }
} else {
    echo "Failed to acquire lock within specified timeout.\n";
}

$redis->close();

在上面的示例中,我们首先创建了一个 RedisLock 类,该类负责封装了获取锁和释放锁的逻辑。在主程序中,我们实例化 RedisLock 对象,并调用 acquireLock 方法来尝试获取锁。如果获取锁成功,则执行需要加锁保护的关键代码块;如果获取锁失败(超时),则输出获取锁失败的信息。

在关键代码块执行完成后,我们使用 finally 块确保无论如何都会释放锁,调用 releaseLock 方法来释放锁资源。

需要注意的是,在实际应用中,分布式锁的实现可能需要考虑更多的复杂性,例如处理锁的续约、处理死锁等情况。此外,Redis 本身提供了一些针对分布式锁的优化方案,例如使用 Redlock 算法来实现更加可靠的分布式锁,你可以根据具体需求来选择合适的方案。
注意:

setnx是一个原子性的命令。它注意作用是指定键不存在时,为该键设置指定值,若键已经存在,则不进行任何操作。如果设置键成功,返回1;若键已经存在,设置失败,返回0。

**锁的释放:**在操作完后需要及时释放锁,否则其他客户端无法获取该锁,从而导致死锁。通常使用del命令释放锁,但在释放前应先检查当前持有锁的客户端是否是自己,避免释放其他客户端的锁。

锁过期时间: 在 Redis 2.6.12 之前,SETNX 只能单纯地在键不存在时设置键值,若要给键设置过期时间,得单独使用 EXPIRE 命令。但这两个命令并非原子操作,在高并发场景下,可能会出现 SETNX 执行成功,然而 EXPIRE 执行失败的情况。这样一来,锁就没办法自动过期,进而引发死锁问题。但从该版本之前set命令得到了增强,能够实现原子性的设置键值,过期时间以及检查键是否存在。

更详细请看
https://blog.csdn.net/qq_42133326/article/details/136454740
https://www.cnblogs.com/phpphp/p/18091067

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐