Java分布式锁的四种实现方式(Redis,Zookeeper,Mysql,Memcached)

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 成功的认为是得到锁。并且设置[过期超时] 时间,保证宕机后,也不会死锁。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

perfect5085

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值