zoukankan      html  css  js  c++  java
  • Redis 应用--分布式锁

    分布式锁是 redis 比较常见的应用之一;

    问题场景:现在有一个一个简单用户的相关操作,一个线程取修改用户状态,首先从数据库读取用户信息,在到内存进行修改,修改完毕进行持久化,单线程这样操作没问题,但是在多线程中,由于读取,修改,持久化 是三个操作,不是原子操作,因此多线程中,可能会发生数据紊乱,对于这种问题可用使用分布式锁限制程序并发执行;分布式锁实现原理:第一个线程先占位,当后续线程进来时发现已被占用,会等待或稍后重试;在 Redis 中,占位一般使用 setnx 进行操作;先进来的线程先占位,线程操作执行完毕在带调用 del 删除指令,释放占位;

    CaooWithJedis  接口
    public interface CaooWithJedis {
        void callJedis(Jedis jedis);
    }

    Redis 类

    public class Redis {
        private JedisPool pool;
    
        GenericObjectPoolConfig conf = new GenericObjectPoolConfig();
        public Redis(){
            pool = new JedisPool(conf,"192.168.134.129",6379,30000,"wdh01");
        }
    
        public void execute(CaooWithJedis caooWithJedis){
            try(Jedis jedis = pool.getResource()){
                caooWithJedis.callJedis(jedis);
            }
        }
    }

    测试代码

    public static void main(String[] args) {
        Redis redis = new Redis();
        redis.execute(jedis -> {
            Long setnx = jedis.setnx("k1", "v1");
            if(setnx == 1){
                // 无人占位
                String set = jedis.set("name", "wdh01");
                String name = jedis.get("name");
                System.out.println(name);
                jedis.del("k1");
            }else{
                //有人占位,停止/暂缓操作
            }
        });
    }

    上面的代码存在一个问题:当代码执行 del 之前出现异常,则会导致 del 未执行,k1 则无法释放,后面的请求也会被阻塞,分布式锁也无法释放;为此 可用为 k1 设置过期时间,确保一定时间后 分布式锁会被释放;

    public static void main(String[] args) {
        Redis redis = new Redis();
        redis.execute(jedis -> {
            Long setnx = jedis.setnx("k1", "v1");
            if(setnx == 1){
                //给锁设置过期时间 10s,即使程序出现问题,10 s后 k1 将被释放
                jedis.expire("k1",10);
                // 无人占位
                String set = jedis.set("name", "wdh01");
                String name = jedis.get("name");
                System.out.println(name);
                jedis.del("k1");
            }else{
                //有人占位,停止/暂缓操作
            }
        });
    }

    这样改造后还有一个问题,在获取锁和设置过期时间之间如果程序或服务器挂掉了,此时锁也将无法得到释放会造成死锁;获取锁和设置过期时间是两个操作;不具备原子性,为了解决此问题,自 Redis 2.8 开始 setnx 和 expire 可用通过一个命令一起执行,下面对上述代码做进一步改进

    public static void main(String[] args) {
        Redis redis = new Redis();
        redis.execute(jedis -> {
            String set = jedis.set("k1","v1",new SetParams().nx().ex(10));
            if(set != null && "OK".equals(set)){
                //给锁设置过期时间 10s,即使程序出现问题,10 s后 k1 将被释放
                jedis.expire("k1",10);
                // 无人占位
                jedis.set("name", "wdh01");
                String name = jedis.get("name");
                System.out.println(name);
                jedis.del("k1");
            }else{
                //有人占位,停止/暂缓操作
            }
        });
    }
  • 相关阅读:
    jQuery之.on()方法
    18款 非常实用 jquery幻灯片图片切换
    jquery 处理字符串
    JS中document.createElement()用法及注意事项
    jquery 创建 SVG DOM 的处理方法
    jQuery append xmlNode 修改 xml 内容
    jQuery与XML
    浏览器中的XML与JavaScript
    DOM(文本对象模型)简介
    用jQuery 处理XML-- jQuery与XML
  • 原文地址:https://www.cnblogs.com/wdh01/p/12853489.html
Copyright © 2011-2022 走看看