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连用
- redis:基于redis实现分布式锁,lua脚本(二)
7.总结
- 简单的基于redis分布式锁完成。如果当一个线程未能获得锁,需要 在次重新获取锁,可以采取递归的方式,在获取锁失败重新回调lock方法。