分布式锁的几种实现方式:

发布时间:2024年01月15日

一:通过redis实现

redis是基于单线程,在某个时刻只会有一个线程执行命令,可以利用set原子性的操作,配合set nx(RedisStringCommands.SetOption.SET_IF_ABSENT) ,这样,当多个线程或多个节点尝试获取锁时,只有一个可以成功,其他的会因为锁已存在而获取失败。这种方式通过 Redis 来实现分布式锁,可以确保在不同的应用实例或节点之间同步对共享资源的访问,从而避免并发冲突。
优点:实现简单,在内存的中获取的锁,很快。


缺点:锁有点重,对AB俩个线程,A在某一时刻获取了锁,B线程只能等待。B线程需要阻塞。
可能出现的问题:会造成死锁的情况,当A竞争到了锁,如果A在此刻由于内部异常造成A挂了,没有释放掉锁,就会造成B死锁。


死锁处理方案:
1、设置一个key的TTL过期时间。
2、处理完之后,主动将锁释放(将key del掉)
3、当然除了以上俩种常用的处理方式还可以使用乐观锁的处理方式。乐观锁通过维护一个版本号,如果版本号和锁的版本号一直即可获得锁,和其他的乐观锁类似,乐观锁在一定程度上可以防止死锁,但也会存在缺点:系统必要的开销会增大,若版本号不一致,会一直比对版本号,若锁被频繁抢夺可能对系统的性能产生影响。
4、使用红锁redLock,基本思想:在多个redis节点中,使用全局唯一的key,设置不同的value(可以为uuid、时间戳等),当set的value和携带的value大部分节点的值一致则说明获取到锁成功。(为啥要大部分,不要完全?因为在不同的节点setkey可能会有网络延迟,时钟漂移等情况。或者极端情况下,高并发多个节点同时set成功)

二、通过MQ实现

1、可以通过设置MQ队列

channel.queueDeclare("myLockQueue", false, true, false, arguments);

将参数设置 exclusive=truedurable=false,确保它是一个独占队列只能有一个连接且非持久化的队列。

在绑定了此队列的消费者,获取锁的时候,在同一时刻只能有一个消费者能够去消费此队列里面的消息,在消费者消费完之后就将队列的里面的锁重新publish上去。等待第二个消费者进行消费。

锁的产生:

  • 当一个消费者需要获取锁时,它向队列发送一个消息,表示它要获取锁。
  • 由于队列是独占的,同一时刻只能有一个消费者能够消费队列中的消息。

锁的释放:

当获取到锁的消费者获取到之后,就重新将“锁”消息publish上去,等待下一个消费者获取。

基于MQ的方式实现的分布式锁也会产生一些问题:

1、当消息丢失了,如果时在消费者消费完之后往队列里面添加释放锁的消息,消息没有推送到队列里面去,就会造成其他已经进入队列想要获取到锁的消费者进行死锁或者锁无法释放。基于这种问题的解决方案可以使用重试机制,在一定次数的重试方式避免引发上述问题。

2、在并发的场景下,由于MQ的消息是异步的,本身在获取锁的时候就会带来时间的延迟问题,某个消息丢失,消费者A和消费者B在处理的消息时,消息的状态不一致,导致消费者A误以为拿到了锁,而继续业务逻辑操作。

文章来源:https://blog.csdn.net/weixin_52875557/article/details/135606459
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。