分享

Redis分布式锁 命令

 不忘初心g2x5dp 2019-11-27

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 有效期组成

特点

支持分布式

可以更细粒度的控制

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

应用场景

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

这个形参 key  可以是 商品id

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

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

第 34和35 行就是 锁过期了

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

总结

简化版 redis 分布式锁

就是一个setnx 动作

简化版redis 锁的缺点

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

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

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

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

进化版 redis锁

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

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

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

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

在多线程 情况下 讨论

如果多线程  进入到上图 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锁的 方式

加锁

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

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

从第 62行到 65行

解锁

就 第85行的操作

分布式锁 流程图

这个优化版的流程这里说明一下,如果获取锁成功,那么按照流程执行业务逻辑,执行完毕,删除锁。如果没有获取到锁,继续判断时间戳,看是否可以重置并获取到锁,先 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 分布式锁流程图 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多