Redis——》锁被别人释放

推荐链接:
    总结——》【Java】
    总结——》【Mysql】
    总结——》【Redis】
    总结——》【Kafka】
    总结——》【Spring】
    总结——》【SpringBoot】
    总结——》【MyBatis、MyBatis-Plus】
    总结——》【Linux】
    总结——》【MongoDB】
    总结——》【Elasticsearch】

Redis——》锁被别人释放

  • 一、锁被别人释放的场景
  • 二、如何避免锁被别人释放
    • 1、GET获取锁 + DEL释放锁
      • (1)实现
      • (2)优点
      • (3)缺点
    • 2、Lua脚本
      • (1)实现
        • <1>创建Lua脚本unlock.script
        • <2>执行Lua脚本unlock.script
      • (2)优点
  • 三、其它
    • 1、增大过期时间是否可以避免锁被别人释放?

一、锁被别人释放的场景

SET lock_key 1 EX 10 NX // 10s后自动过期

执行以上命令,给锁加自动过期时间,但每个客户端在释放锁时,都是无脑操作,并没有检查这把锁是否还归自己持有,所以就会发生释放别人锁的风险。
Redis——》锁被别人释放_第1张图片

  1. 客户端1加锁成功,开始操作共享资源
  2. 客户端1操作共享资源耗时太久,超过了锁的过期时间,锁失效(锁被自动释放)
  3. 客户端2加锁成功,开始操作共享资源
  4. 客户端1操作共享资源完成,在finally块中手动释放锁,但此时它释放的是客户端2的锁。

二、如何避免锁被别人释放

解决方案:客户端在加锁时,设置一个只有自己知道的唯一标识进去。
唯一标识可以采用:

  • 自己的线程ID
  • UUID(随机且唯一)

1、GET获取锁 + DEL释放锁

(1)实现

// 加锁
SET lock_key unique_value EX 10 NX

//释放锁,先判断这把锁是否归自己持有,比较unique_value是否相等,避免误释放
if redis.get("lock_key") == unique_value then
    return redis.del("lock_key")

(2)优点

解决了锁被别人释放的问题

(3)缺点

释放锁使用的是GET + DEL两条命令,这两条命令不能保证是原子操作(一起成功),有可能执行第一条GET成功,但第二条DEL却执行失败。

2、Lua脚本

因为Redis处理每个请求是单线程执行的,在执行一个Lua脚本时其它请求必须等待,直到这个Lua脚本处理完成,这样一来GET+DEL之间就不会有其他命令执行了。

(1)实现

<1>创建Lua脚本unlock.script

参数 描述
KEYS[1] 表示lock_key
ARGV[1] 表示当前客户端的唯一标识
//Lua脚本语言
//释放锁,先判断这把锁是否归自己持有,比较unique_value是否相等,避免误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

<2>执行Lua脚本unlock.script

redis-cli --eval unlock.script lock_key , unique_value 

(2)优点

解决了锁被别人释放的问题

三、其它

1、增大过期时间是否可以避免锁被别人释放?

如果只是一味增大过期时间,只能缓解问题降低出现问题的概率,依旧无法彻底解决问题。
原因在于客户端在拿到锁之后,在操作共享资源时,遇到的场景是很复杂的,既然是预估的时间,也只能是大致的计算,不可能覆盖所有导致耗时变长的场景。

你可能感兴趣的:(Redis,redis,锁,释放,lua,过期)