java mysql 分布式锁_java实现分布式锁

本文介绍了分布式系统中为保证数据一致性而引入的分布式锁概念。在单机时代,线程间的锁机制可以解决共享资源的并发问题,但在分布式环境中,需要分布式锁来实现多客户端互斥访问。文章详细讨论了基于数据库(乐观锁、悲观锁)、Redis和ZooKeeper实现分布式锁的方法,其中数据库实现包括乐观锁和悲观锁的原理及代码示例,Redis实现则涉及SETNX、GETSET、EXPIRE等命令的应用,而ZooKeeper实现则利用节点的唯一性来确保锁的独占性。
摘要由CSDN通过智能技术生成

1.前言

大多数互联网系统是分布式部署的,分布式部署解决了高并发高可用的问题,但是由此带来了数据一致性问题。

当某个资源在多系统之间,被共享操作的时候,为了保证这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端操作,不能并发的执行,否者就会出现同一时刻有客户端写,别的客户端在读,两者访问到的数据就不一致了。

2.我们为什么需要分布式锁

在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行加锁,当使用完资源之后,再解锁,其它线程就可以接着使用了。例如,在JAVA中,甚至专门提供了一些处理锁机制的一些API(synchronize/Lock等)。

但是到了分布式系统的时代,这种线程之间的锁机制,就没作用了,应用程序会有多份,并且部署在不同的机器上,这些资源已经不是在同一进程的不同线程间共享,而是属于多进程之间共享的资源。

因此,为了解决这个问题,我们就必须引入「分布式锁」。

分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。

分布式锁要满足哪些要求呢?

排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法获取

避免死锁:这把锁在一段有限的时间之后,一定会被释放(正常释放或异常释放)

高可用:获取或释放锁的机制必须高可用且性能佳

而且最好是可重入锁。

3.分布式锁的实现方式有哪些

目前主流的有三种,从实现的复杂度上来看,从上往下难度依次增加:

基于数据库实现

基于Redis实现

基于ZooKeeper实现

无论哪种方式,其实都不完美,依旧要根据咱们业务的实际场景来选择。

方案1 基于数据库实现

基于数据库来做分布式锁的话,通常有两种做法:

基于数据库的乐观锁

基于数据库的悲观锁

我们先来看一下如何基于「乐观锁」来实现:

乐观锁机制其实就是在数据库表中引入一个版本号(version)字段来实现的。

当我们要从数据库中读取数据的时候,同时把这个version字段也读出来,如果要对读出来的数据进行更新后写回数据库,则需要将version加1,同时将新的数据与新的version更新到数据表中,且必须在更新的时候同时检查目前数据库里version值是不是之前的那个version,如果是,则正常更新。如果不是,则更新失败,说明在这个过程中有其它的进程去更新过数据了。

乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的left_count减1,在并发的情况下如何要保证left_count不为负数,乐观锁的实现方式为在红包表上添加一个版本号字段(version),默认为0。

异常实现流程

--可能会发生的异常情况--线程1查询,当前left_count为1,则有记录select * from t_bonus where id = 10001 and left_count > 0

--线程2查询,当前left_count为1,也有记录select * from t_bonus where id = 10001 and left_count > 0

--线程1完成领取记录,修改left_count为0,

update t_bonusset left_count = left_count - 1 where id = 10001

-- 线程2完成领取记录,修改left_count为-1,产生脏数据

update t_bonusset left_count = left_count - 1 where id = 10001

通过乐观锁实现

--添加版本号控制字段

ALTER TABLE table ADD COLUMN version INT DEFAULT'0'NOT NULL AFTER t_bonus;--线程1查询,当前left_count为1,则有记录,当前版本号为1234select left_count, version from t_bonus where id = 10001 and left_count > 0

--线程2查询,当前left_count为1,有记录,当前版本号为1234select left_count, version from t_bonus where id = 10001 and left_count > 0

--线程1,更新完成后当前的version为1235,update状态为1,更新成功

update t_bonusset version = 1235, left_count = left_count-1 where id = 10001 and version = 1234

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值