程序员:基于redis实现分布式锁,setnx和setex
1.分布式锁是什么?
分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现
如果不同的系统或同一个系统的不同主机之间共享了某一个资源时,往往通过互斥来防止彼此干扰
2.分布锁设计目的
可以保证在分布式部署的应用集群中,同个方法在同操作只能被一台机器上的一个线程执行。
3.分布式锁实现方案分析
在获取锁的时候使用setnx,(Set If Not Exist)当且仅当key不存在进行set,成功返回1,失败返回0
判断是否成功set了锁,成功就为锁设置超时时间,使用setex
执行结束释放锁,判断当前线程是否获得了锁,获得则del
4.本地起两个服务节点作为演示。演示代码如下:
本文采用定时调度模拟线程去获取锁(链接:详解Scheduled定时调度)
使用-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连用 。
7.总结
简单的基于redis分布式锁完成。如果当一个线程未能获得锁,需要 在次重新获取锁,可以采取递归的方式,在获取锁失败重新回调lock方法。