redis:基于redis实现分布式锁,setnx和setex(一)

1.分布式锁是什么?

  • 分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一 种锁实现
  • 如果不同的系统或同一个系统的不同主机之间共享了某一个资源时,往往通过互斥来防止彼此干扰

2.分布锁设计目的

  • 可以保证在分布式部署的应用集群中,同个方法在同 操作只能被一 台机器上的一 个线程执行。

 3.分布式锁实现方案分析

  • 在获取锁的时候使用setnx,(Set If Not Exist)当且仅当key不存在进行set,成功返回1,失败返回0
  • 判断是否成功set了锁,成功就为锁设置超时时间,使用setex
  • 执行结束释放锁,判断当前线程是否获得了锁,获得则del

4.本地起两个服务节点作为演示。演示代码如下:

  1. 本文采用定时调度模拟线程去获取锁(链接:详解Scheduled定时调度)
  2. 使用-Dserver.port=9527,-Dserver.port=9528开启多个节点
@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${server.port}")
    private String port;

    private Boolean absent;

    @Scheduled(cron = "0/5 * * * * *")
    public void lock() {

        String lock = "LockNxExJob";

        try {

            // 获取锁
            absent = redisTemplate.opsForValue ().setIfAbsent (lock, port);

            if (!absent) {
                System.out.println (String.format ("获取锁失败!被%s拿走", redisTemplate.opsForValue ().get (lock)));
            } else {
                redisTemplate.opsForValue ().set (lock, port, 3600);
                System.out.println (String.format ("获取锁成功!值为:%s", redisTemplate.opsForValue ().get (lock)));
            }

        } catch (Exception e) {
            e.printStackTrace ();
        } finally {
            // 释放锁
            if (absent) redisTemplate.delete (lock);
        }

    }

}

9527端口控制台输出:

  • 获取锁失败!被9528拿走
  • 获取锁成功!值为:9527
  • 获取锁成功!值为:9527
  • 获取锁成功!值为:9527

9528端口控制台输出:

  • 获取锁成功!值为:9528
  • 获取锁失败!被9527拿走
  • 获取锁失败!被9527拿走
  • 获取锁失败!被9527拿走

5.问题剖析:

  • 在setnx成功之后,服务发生了宕机,此时没能完成设置超时时间,此key则一直存在,无法得到释放,其他服务节点则永远无法获得锁,出现死锁。

6.解决方案:

  • 怎么一次性执行过一条命令而不会出现问题,采用Lua脚本。Redis从2.6之后支持setnx、setex连用 
  • redis:基于redis实现分布式锁,lua脚本(二)

7.总结

  • 简单的基于redis分布式锁完成。如果当一个线程未能获得锁,需要 在次重新获取锁,可以采取递归的方式,在获取锁失败重新回调lock方法。

你可能感兴趣的:(redis)