Redis分布式锁 命令

Redis分布式锁 命令

注意:

这篇博客系本人将以前在OneNote的笔记搬到CSDN ,由于OneNote操作的特殊性 会存在很多截图便于说明只用。

不喜勿喷。

  • setnx当且仅当 key 不存在。若给定的 key 已经存在,则 setnx不做任何动作。setnx 是『set if not exists』(如果不存在,则 set)的简写,setnx 具有原子性。 
    getset先 get 旧值,后set 新值,并返回 key 的旧值(old value),具有原子性。当 key 存在但不是字符串类型时,返回一个错误;当key 不存在的时候,返回nil ,在Java里就是 null。 
    expire 设置 key 的有效期 
    del 删除 key
  • 与时间戳的结合 
    分布式锁的值是按 系统当前时间 System.currentTimeMillis()+Key 有效期组成

 

特点

 

支持分布式

可以更细粒度的控制

多台机器上多个进程对一个数据进行操作的互斥

 

 

 

应用场景

Redis分布式锁 命令_第1张图片

 

将 用户下单减库存这个动作 必须要单线程操作。

 

这个形参 key  可以是 商品id

第35行的判断是 根据key获取到的value  如果小于当前时间。

第34行的判断是  根据key获取到的value  如果是空值

第 34和35 行就是 锁过期了

Redis分布式锁 命令_第2张图片

如果锁过期   就获取上一个锁的事件 并比较

Redis分布式锁 命令_第3张图片

 

总结

 

简化版 redis 分布式锁

就是一个setnx 动作

Redis分布式锁 命令_第4张图片

 

 

简化版redis 锁的缺点

如果 获取锁成功 但是后面的 下单 减库存等操作失败抛出异常(偶发),

这样会导致 解锁操作 没法进行,因为已经抛出异常了。

这个时候 已经加锁但是无法解锁,下次一个新请求过来,setnx 是会失败的。

这样 就造成死锁发生,下图的逻辑 就永远停留在这一步加锁这一步但是永远失败

Redis分布式锁 命令_第5张图片

 

进化版 redis锁

在上上图简化版redis锁的 基础上 增加了 第31到40行的操作。

因为setnx设置了过期时间,31行得到 lockA 。

在 第33到34行 判断 锁是否过期,(lockA 为空或则小于当前时间),

如果 锁过期 就会进入 36行  比较 上一个锁的日期 就有机会返回true 从而得到锁 并有机会释放锁。

Redis分布式锁 命令_第6张图片

 

在多线程 情况下 讨论

如果多线程  进入到上图 27行 (假设两个线程)

两个线程 都会进行判断,假设锁都被占用,所以他们不会进入28行,直接进入31行,因为锁已经被一个线程占用了,

这个时候 假设,那个拿到锁的线程 的 currentValue 值是A ,而两个线程的 value 值是B。

如果 那个拿到锁的线程 的锁超时,就会进入下图 36行。

这个36行 采用了 getset 方法,这个value 的值是B (这个36行的 getset只会有一个线程去执行原子性) ,

假设是第一个线程去执行,拿到的  oldValue值是A 这个currentValue 值也是 A ,如果判断相等就会返回true 。这个时候就是第一个线程拿到了锁。

接着 第二个线程又去执行这个 36行的代码,拿到的  oldValue值是B 这个currentValue 值也是 A ,就不相等 就拿不到锁。

结果就是多线程情况下 只有一个线程恩给你拿到锁。

Redis分布式锁 命令_第7张图片

 

 

 

解锁操作

Redis分布式锁 命令_第8张图片

 

 

运用 redis锁的 方式

加锁

这个 Timeout 是 一个超时时间,用static  和final 修饰 成员变量

注意 这个setnx 的key  是商品id  是属于细粒度的操作

从第 62行到 65行

Redis分布式锁 命令_第9张图片

 

解锁

就 第85行的操作

Redis分布式锁 命令_第10张图片

 

 

分布式锁 流程图

Redis分布式锁 命令_第11张图片

 

Redis分布式锁 命令_第12张图片

 

 

 

 

这个优化版的流程这里说明一下,如果获取锁成功,那么按照流程执行业务逻辑,执行完毕,删除锁。如果没有获取到锁,继续判断时间戳,看是否可以重置并获取到锁,先 get 得到当前的值,并且比较一下当前的系统时间和得到的值得大小,如果当前时间大于值,说明锁已经过期了,接下来就执行 getset 操作,将该key的值重新设置一下,如果返回的旧值不存在,说明这个key 已经被删除了,或者这个旧值存在,并且和之前get 的值相同,说明此时真正的获取到锁了,接下来执行相应的业务逻辑。

 

优化版 redis 分布式锁的解释

解释下: get(lockKey) 之后的业务逻辑

   当setnx 失败时 不直接结束,而是通过 get(lockKey) 拿到 最开始 setnx设置的currentTime 和 timeout的 value值  叫做 lockA 。

 如果不为空而且当前时间大于 这个lockA  ,也就是说当 setnx的时候 这个值 已经超时(timeout),我就有权利获取这个锁的。 如果条件满足就来到下面分支的true 这里调用 getset方法,key是 lockKey,但是value 是当前的 curenTime + lockA 重新赋值。这个getset 方法会返回 lockB , 如果这个lockB 是空则代表这个 lockB已经没有了这时我是可以获取到锁的,或则 lockA  等于  lockB  表示这个过程中这个锁是没有变化的如果这个条件满足的话 就可以真正的获取到锁了。 

如果当前时间并不大于 lockA  表示结束分布式锁这个流程。

  • Redis 分布式锁优化版流程 
  • 与时间戳的结合 
    分布式锁的值是按 系统当前时间 System.currentTimeMillis()+Key 有效期组成
  • Redis 分布式锁流程图 

 

 

 

 

 

你可能感兴趣的:(JAVA)