0%

redis分布式锁

2018年3月10日 下午12:10

这个代码对应于tack/CloseOrderTask.java类,我这里就先不分析代码了!

  1. 我的通俗理解:小明和小王逛街累了抢一个椅子,当然是谁先抢上谁坐,说明椅子有主了。但是如果一个人坐在那里好多个小时,他始终不离开椅子,自己玩手机,另一个人就得一直等着他。等上一个小时,站着的哪个就火了,直接踹开那家伙,自己就坐了上去。
    1. 离开椅子的标志是:在redis中删除了CLOSE_ORDER_TASK_LOCK这个变量。
    2. 抢上椅子的标志为:在redis中设置了CLOSE_ORDER_TASK_LOCK这个变量
  2. 其实expire就可以起到防止死锁的作用,让50秒以后redis就会自动删除这个键。我理解这是一个双重保险。

版本一:

  1. 第一个版本:不使用redis,只是使用Spring Schedule使关单程序周期执行。但是由于我们是tomcat集群,这个关单程序会在两个tomcat同时执行,并且同时操作数据库。
  2. 一定会带来的问题:定时任务同时执行,对数据库的访问造成数据混乱

版本二:

  1. 第二个版本:使用redis做为分布式的锁,即使这个关单程序会在两个tomcat同时执行,但是会由于有这个锁的存在,使他们走不同的分支,只有其中一个分支可以执行关单程序业务流程,去操作数据库。如下图,这样就不会造成数据库的访问造成数据混乱。
  2. 极端条件下可能出现的问题:当我们setnx将“锁”放入redis之后,此时停止tomcat,那么原先放入的“锁”就会一直存在reids中,不会删除,以后由于这个“锁”的存在,永远不会执行真正的业务。最终造成死锁
  3. 关键:
    1. 使用了setnx这个原子性的操作,这就保证了即使同时执行,也会有一个先来后到,一个走左分支,一个走右分支

版本三:

  1. 第三个版本:假设,当我们setnx将“锁”放入redis之后,此时停止tomcat。那么此时两个tomcat中的同时执行的程序,永远都会走到“获取锁失败”的分支。那么我们就需要在执行了“获取锁失败”的分支中解决这个问题,在redis中这个“死锁”条件存在的情况下,也可以执行相关业务
  2. 关键:
    1. 在setnx时,我们的的“锁”key-value值要设置这个锁的超时时间,当然这个只是一个字符串的类型,是看的,而不是他自动会起作用的。
    2. 第一种情况:
      1. 还没有超过了这个“死锁”的超时时间
      2. 我们规定此时已然不能执行业务,必须等待这个“死锁”的时间到了
    3. 第二种情况:
      1. 已经超过了这个“死锁”的超时时间,我们此时就可以进行业务操作了,但是此时同样还是两个tomcat都要请求执行,那给谁呢?这不是回到了一开始的情况了吗
      2. 关键:
        1. 在第二个版本中我们使用原子操作setnx,来确定到底是哪个tomcat执行业务,在这里我们使用另一原子操作getset来区分。
      3. 总结:
        1. 对于判断一来说,两个tomcat要true都true,要false都false
        2. 对于判断二来说,一个tomcat是true一个tomcat是false

总结:

  1. redis 两个原子性操作是实现的关键
  2. Redis的中setnx的值,就是”锁”
  3. 一个锁,有两个时间
    1. 一个是setnx时的字符串value
    2. 一个是redis本身可以对单个数据设置的超时时间