企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 一、锁实现的注意点 1. 互斥: 任意时刻, 只能有一个客户端获得锁 2. 不会死锁: 客户端持有锁期间崩溃, 没有主动解除锁, 能保证后续的其他客户端获得锁 3. 锁归属标识: 加锁和解锁的必须是同一个客户端, 客户端不能解掉非自己持有的锁(锁应具备标识) # 二、 加锁 ``` $redis = new Redis(); $redis->pconnect("127.0.0.1", 6379); $redis->auth("password"); // 密码验证 $redis->select(1); // 选择所使用的数据库, 默认有16个 $key = "..."; $value = "..."; $expire = 3; // 参数解释 ↓ // $value 加锁的客户端请求标识, 必须保证在所有获取锁清秋的客户端里保持唯一, 满足上面的第3个条件: 加锁/解锁的是同一客户端 // "NX" 仅在key不存在时加锁, 满足条件1: 互斥型 // "EX" 设置锁过期时间, 满足条件2: 避免死锁 $redis->set($key, $value, ["NX", "EX" => $expire]) ``` # 三、解锁 1. 使用lua脚本. ``` $key = "..."; $identification = "..."; // KEYS 和 ARGV 是lua脚本中的全局变量 $script = <<< EOF if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end EOF; # $result = $redis->eval($script, [$key, $identification], 1); // 返回结果 >0 表示解锁成功 // php中参数的传递顺序与标准不一样, 注意区分 // 第2个参数表示传入的 KEYS 和 ARGV, 通过第3个参数来区分, KEYS 在前, ARGV 在后 // 第3个参数表示传入的 KEYS 的个数 $result = $redis->evaluate($script, [$key, $identification], 1); ``` 2. 使用Lua脚本的原因: * 避免误删其他客户端加的锁: 某个客户端获取锁后做其他操作过久导致锁被自动释放, 这时候要避免这个客户端删除已经被其他客户端获取的锁, 这就用到了锁的标识. * lua 脚本中执行`get`和`del`是原子性的, 整个lua脚本会被当做一条命令来执行 * 即使`get`后锁刚好过期, 此时也不会被其他客户端加锁 **注意:** eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。 由于 script 执行的原子性, 所以不要在script中执行过长开销的程序,否则会验证影响其它请求的执行。 3. 解锁容易错误的点: * 直接`del`删除键 原因: 可能移除掉其他客户端加的锁(在自己的锁已过期情况下) * `get`判断锁归属, 若符合再`del` 原因: 非原子性操作, 若在`get`后锁过期了, 此时别的客户端进行加锁操作, 这里的`del`就会错误的将其他客户端加的锁解开.