1,使用Redis做分布式锁:

利用SETNX添加一个锁,并设置锁的释放时间。

问题:

a,某个机器实例的任务执行时长超时了,超过了锁释放的时间,会造成其他机器实例获取到该锁并执行任务。任务被同时执行。

b,Redis的部署模式:如果是单实例,或者是master-slave模式。 Redis可能会挂(概率很小),或者只是针对master节点加锁,如果master节点故障,发生master,slave切换,锁丢失。

 

解决方案:

1,针对问题a

* 如果任务时间超时,可以设置告警,人工进行干预。

* 或者在当前机器实例上,频繁的去get锁,如果锁属于自己,则延长锁的释放时间。

2,针对问题b

* 如果是master-slave模式,在每个节点都建立锁,如果是Cluster模式,在超过一半的节点上都建立锁。

 

如果业务对分布式锁出错可以容忍,不是那么强烈的一致性,那就使用Redis的简单实现,因为Redis可以支撑

很高的并发。

比如一些周期性定时任务,例如同步数据的,处理异常情况等等,避免多个实例跑浪费机器资源的。

 

如果业务对分布式锁有一定要求,当然不是100%的强一致性,而且并发特别高,可以考试使用

Redisson, 他具有很高的数据一致性,可以达到99.99%,而且性能特别好。

可以使用Redisson的多个Redis实例的分布式锁。

Config config = new Config(); 
config.useClusterServers().addNodeAddress("redis://192.168.31.101:7001") 
.addNodeAddress("redis://192.168.31.101:7002") 
.addNodeAddress("redis://192.168.31.101:7003") 
.addNodeAddress("redis://192.168.31.102:7001") 
.addNodeAddress("redis://192.168.31.102:7002") 
.addNodeAddress("redis://192.168.31.102:7003"); 
RedissonClient redisson = Redisson.create(config); 
RLock lock = redisson.getLock("anyLock"); 
lock.lock(); 
lock.unlock();

它的 API 中的 Lock 和 Unlock 即可完成分布式锁:

Redisson 中有一个 Watchdog 的概念,翻译过来就是看门狗,它会在你获取锁之后,每隔 10s 帮你把 Key 的超时时间设为 30s。

Redisson 的“看门狗”逻辑保证了没有死锁发生。(如果机器宕机了,看门狗也就没了。此时就不会延长 Key 的过期时间,到了 30s 之后就会自动过期了,其他线程可以获取到锁)

 

2,基于Zookeeper实现分布式锁

ZK:提供配置管理,分布式协同,命名的中心化服务。

ZK的节点类型:持久节点,临时节点

顺序节点:持久顺序节点,临时顺序节点

 

分布式锁实现原理:为锁创建一个持久化节点,例如:/lock

在这个节点下,每个Client创建临时顺序节点,如:/lock/client-001,/lock/client-002,/lock/client-003

临时节点,如果Client会话结束,或者断开,ZK自动删除临时节点。

Client获取/lock下所有的子节点列表,判断当前创建的子节点列表序号是不是最小,如果是,认为获取该锁。

如果不是,监听比自己小一个的节点。直到获得节点变更通知后重复检查节点序号。

 

这里,为什么监听比自己小一个的节点,而不是最小序号的节点,因为,如果/lock下有1000个节点的话,

当最小节点有变更通知,ZK需要通知其它999个节点。ZK会发生阻塞,Client也没有必要同时去争抢锁。

 

3,基于Mysql数据库实现分布式锁:

悲观锁:select ... for update

首先设置Mysql为非auto commit 模式,放在一个事务执行。

  1. 第一步查询锁定一行数据:select * from table where id = 1 for update 锁定 id=1 的行。
  2. 第二步:操作和这一行数据关联的业务,比如:insert, 或者 update 等等。
  3. 第三步:提交事务。id=1 的行解锁。

当然,在事务中,只有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一笔数据时会等待其它事务结束后才执行,一般SELECT … 则不受此影响。

 

4,基于Memcached的分布式锁实现,和Redis最简单的分布式锁实现功能效果一致。

Memcached的分布式完全是依赖客户端的一致性哈希算法来达到分布式的存储,在Memcached服务端,所有的操作都是原子性的。

我们利用add函数,add会添加第一个到达的值,并返回true,后续的添加则都会返回false。

利用这个原理,可以先定义一个 锁 LockKEY ,add 成功的认为是得到锁。并且设置[过期超时] 时间,保证宕机后,也不会死锁。