zoukankan      html  css  js  c++  java
  • Redis分布式锁的一点小理解

    1、在分布式系统中,我们使用锁机制只能保证同一个JVM中一次只有一个线程访问,但是在分布式的系统中锁就不起作用了,这时候就要用到分布式锁(有多种,这里指 redis)

    2、在 redis当中可以使用命令 setnx(key, value)来实现分布式锁

      setnx:当key不存在的时候设置成功,返回1,若存在的话返回0表示失败。使用这个命令的话要搭配 expire(key, time)来设置过期时间,但是这种组合存在问题,如下:

       try {
            if (redisclient. setnx(key 1) == 1) { //1
                redisClient.expire(key, 1000);//2
            }
        } finally {
            redisClient.del(key);
        }

      如上代码,看上去没什么问题,但是极端情况下如果在1处执行完毕2处还没执行这时候这台机器宕机了,那么这个锁将是无期限的,且不会被删除,也就是说设置setnx和expire是两个命令,不具备原子性 针对这个问题,可以使用 redis2.6版本之后的命令set(key, value, expirefime,NX)来解决,这个命令跟 setnx一样,但是多了过期时间,可以很好的解决这个问题。

    3、解决这个问题之后,还存在着一个问题:如果在过期时间内程序代码没执行完,那么其他其他机器线程获得这个锁,这样会造成同时有两个线程执行一段代码,并且A机器(过期还没执行完)中finally会删除key,导致误删到B机器锁(当前获得锁的机器)的情况

      1、此时我们可以在相同的机器上开一个守护线程(如上面例子就在A机器再开一个守护线程),这个线程主要作用是在key快过期的时候进行续命操作,保证代码执行完毕。 2、关于误删,我们可以把value设成线程id,删除前判断一下是否是自己的线程ID,是的话再执行删除,如下面代码

         try {
                ....
            } finally {
                if (threadId equals(redisclient get(key)) {//1
                    redisclient.del(key);//2
                }
            }

      这时一般情况都没问题,但是这里的1和2又跟前面的问题类似---不具备原子性,所以还是有出错的可能,但是 redis中没有支持获取删除的原子性命令,该怎么解决呢? 我们可以通过工Lua脚本来解决,例如本例中可以像下面这么写

       String luascript = "if redis call('get', KEYS[1])==ARGV[l] then return redis.call('del', KEYS[1]) else return 0 end";
         redisClient.eval(luaScript, Collections.singletonList(key), Collections.singletonList(threadId));

    4、到这里,基本就没什么问题了,最终的代码如下

      try {
            String luascript = "if redis call('get, KeY[1])==ARGV[l] then return redis.call('del', KEY[1]) else return 0 end";
            string threadId = Thread.currentThread() getId();
            while (redisclient set(key, threadId, 1000, NX) == 1) {
             // dosomething()
            }
        } finally {
            redisClient.eval(luaScript, Collections.singletonList(key), Collections.singletonList(threadId));
        }

     

  • 相关阅读:
    团队作业 总结
    个人作业 Alpha项目测试
    第二次作业
    交互式多媒体图书平台的设计与实现
    基于VS Code的C++语言的构建调试环境搭建指南
    码农的自我修养之必备技能 学习笔记
    工程化编程实战callback接口学习
    如何测评一个软件工程师的计算机网络知识水平和编程能力
    深入理解TCP协议及其源代码
    Socket与系统调用深度分析
  • 原文地址:https://www.cnblogs.com/zhangweicheng/p/11495971.html
Copyright © 2011-2022 走看看