分布式锁实现方式

分布式锁实现方式

1.基于数据库

创建一个专门的锁表,通过操作数据来实现。基于数据库的锁性能相对较差,一般都是首先排除的方案,必要才用。

  1. 基于唯一索引实现

    以方法名为唯一索引,每次使用时插入,结束时删除,当无法插入时表示有人占有锁。这种方案的问题是服务挂掉可能数据一直存在,导致锁一直没有删除释放,需要定时清理。

  2. 基于悲观锁实现

    通过悲观锁查询一条记录锁定,当其他线程查询时必须block等待,等待超时则失败(InnoDb默认等待时间50s,innodb_lock_wait_timeout设置事务锁超时时间)。悲观锁可以严格保证数据访问的安全。但是缺点也明显,即每次请求都会额外产生加锁的开销且未获取到锁的请求将会阻塞等待锁的获取,在高并发环境下,容易造成大量请求阻塞,影响系统可用性。另外,悲观锁使用不当还可能产生死锁的情况。

  3. 基于乐观锁实现

    设置version字段,控制版本,每次使用前查询,使用后根据version值更新version+1,如果更新影响行数为0说明资源已经被修改了。乐观锁适合并发量不高的情况,并发量高会大量更新,增加数据库压力。

2. 基于缓存

  1. 基于redis

    加锁实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间。

    1
    2
    3
    4
    5
    
    SET lock_key random_value NX PX 5000
    值得注意的是:
     `random_value` 是客户端生成的唯一的字符串
     `NX` 代表只在键不存在时,才对键进行设置操作
     `PX 5000` 设置键的过期时间为5000毫秒
    

    这样,如果上面的命令执行成功,则证明客户端获取到了锁。删除key即释放锁。主要缺点是锁不具有重入性,主从集群时,主还未同步就挂掉,此时从会变为主,此时仍可以获取锁。

  2. 基于redisson

    内部是hash结构,通过keyName来判断锁存不存在,不存在设置值和过期时间加锁成功,如果已存在判断是否为当前线程,不是当前线程失败,是当前线程则计数加1(可重入实现)。加锁和解锁都是通过lua脚本代码来实现的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    //redisson github地址:https://github.com/redisson/redisson
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        config.useSingleServer().setPassword("redis1234");
        final RedissonClient client = Redisson.create(config);  
        RLock lock = client.getLock("lock1");
        try{
            lock.lock();
        }finally{
            lock.unlock();
        }
    }
    

3. 基于zookeeper

创建临时有序节点,每个线程均能创建节点成功,但是其序号不同,只有序号最小的可以拥有锁,其它线程只需要监听比自己序号小的节点状态即可。可以通过curator框架来实现。

基本思路如下:

  1. 在你指定的节点下创建一个锁目录lock

  2. 线程X进来获取锁在lock目录下,并创建临时有序节点

  3. 线程X获取lock目录下所有子节点,并获取比自己小的兄弟节点,如果不存在比自己小的节点,说明当前线程序号最小,顺利获取锁

  4. 此时线程Y进来创建临时节点并获取兄弟节点 ,判断自己是否为最小序号节点,发现不是,于是设置监听(watch)比自己小的节点(这里是为了发生上面说的羊群效应)

  5. 线程X执行完逻辑,删除自己的节点,线程Y监听到节点有变化,进一步判断自己是已经是最小节点,顺利获取锁

### 回答1: Java分布式锁实现方式有多种,常见的包括: 1. 基于Redis的分布式锁:利用Redis单线程的特性,使用SETNX命令创建锁,利用EXPIRE设置锁的过期时间,同时使用DEL命令释放锁,确保锁的释放是原子的。 2. 基于Zookeeper的分布式锁:通过创建临时节点实现分布式锁,当某个服务占用了锁,其它服务将无法创建同名节点,从而保证同一时间只有一个服务占用该锁。 3. 基于数据库的分布式锁:使用数据库表中的一行记录来表示锁状态,使用事务确保锁的获取和释放是原子的。 4. 基于Redisson的分布式锁:Redisson是一个开源的Java分布式框架,提供了对分布式锁的支持,使用SETNX和EXPIRE命令实现锁的创建和过期,同时还提供了自旋锁、可重入锁等高级特性。 以上是Java分布式锁实现方式的几种常见方式,不同的实现方式有着各自的特点和适用场景,需要根据实际需求进行选择。 ### 回答2: Java分布式锁是分布式系统中实现数据同步和控制的关键技术之一,它用于保证多个分布式进程并发访问共享资源时的数据一致性和安全性。分布式锁与普通的锁相比,需要解决跨进程、跨节点的同步和并发控制问题。 Java分布式锁实现方式有以下几种: 1. 基于Zookeeper实现分布式锁 Zookeeper是一个高性能的分布式协调服务,它可以被用来实现分布式锁。Zookeeper的实现原理是基于它的强一致性和顺序性,可以保证多个进程访问同一个分布式锁时的数据同步和控制。 通过创建一个Zookeeper的持久节点来实现分布式锁,使用create()方法来创建节点,如果创建成功则说明获取锁成功。当多个进程同时请求获取锁时,只有一个进程能够创建节点成功,其它进程只能等待。当持有分布式锁的进程退出时,Zookeeper会自动删除对应的节点,其它进程就可以继续请求获取锁。 2. 基于Redis实现分布式锁 Redis是高性能的内存数据库,可以使用它的setnx()命令来实现分布式锁。setnx()命令可以在指定的key不存在时设置key的值,并返回1;如果key已经存在,则返回0。通过这个原子性的操作来实现分布式锁。 当多个进程同时请求获取锁时,只有一个进程能够成功执行setnx()命令,其它进程只能等待。进程在持有锁期间,可以利用Redis的expire()命令来更新锁的过期时间。当持有分布式锁的进程退出时,可以通过delete()命令来删除锁。 3. 基于数据库实现分布式锁 数据库通过ACID特性来保证数据的一致性、并发性和可靠性,可以通过在数据库中创建一个唯一索引来实现分布式锁。当多个进程同时请求获取锁时,只有一个进程能够成功插入唯一索引,其它进程只能等待。当持有分布式锁的进程退出时,可以通过删除索引中对应的记录来释放锁。 不同的实现方式各有优劣。基于Zookeeper的实现方式可以保证分布式锁的一致性和可靠性,但是需要引入额外的依赖;基于Redis可以实现较高性能的分布式锁,但是在高并发条件下可能会存在死锁等问题;基于数据库的实现方式简单,但在高并发条件下也可能会有锁争抢等问题。 总之,在选择分布式锁实现方式时,需要根据业务场景和需求来综合考虑各种因素,选择最适合自己的方式。 ### 回答3: 分布式系统中的并发控制是解决分布式系统中竞争资源的重要问题之一,而分布式锁作为一种并发控制工具,在分布式系统中被广泛采用。Java作为一种常用的编程语言,在分布式锁实现方面也提供了多种解决方案。下面就分别介绍Java分布式锁实现方式。 1. 基于ZooKeeper的分布式锁 ZooKeeper是分布式系统中常用的协调工具,其提供了一套完整的API用于实现分布式锁实现分布式锁的过程中需要创建一个Znode,表示锁,同时用于控制数据的访问。在这个Znode上注册监听器用于接收释放锁的成功/失败事件,从而控制加锁/解锁的过程。 2. 基于Redis的分布式锁 Redis作为一种高性能的Key-Value数据库,其提供了完整的API用于实现分布式锁实现分布式锁的过程中需要在Redis中创建一个Key,利用Redis的SETNX命令进行加锁,同时设置过期时间保证锁的生命周期。在解锁时需要判断是否持有锁并删除对应的Key。 3. 基于数据库的分布式锁 数据库作为分布式系统中常用的数据存储方式,其提供了事务机制用于实现分布式锁。在实现分布式锁的过程中需要在数据库中创建一个表,利用数据库的事务机制实现加锁/解锁,同时需要设置过期时间保证锁的生命周期。 总之,以上三种方式都是常用的Java分布式锁实现方式。选择合适的方法需要综合考虑锁的使用场景、性能需求、可靠性要求等因素。同时,在实现分布式锁的过程中需要注意锁的加锁/解锁的正确性和过期时间的设置,保证分布式系统的并发控制的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值