分布式锁详解

分布式锁的理解

理解分布式锁,核心点是在于要理解共享资源访问的问题。
为了解决这个问题,就需要一种跨服务,跨JVM的锁机制,来控制共享资源的访问,这就是分布式锁。
分布式锁是一种用于在分布式系统中实现多个节点之间协调访问共享资源的机制。在分布式系统中,由于多个节点同时访问共享资源可能出现竞态条件和数据不一致等问题,因此需要引入分布式锁来确保在某一时刻只有一个节点能够操作共享资源,从而保证数据的一致性和正确性。
分布式锁通常具备以下特点和要求:

  1. 互斥性:只有一个节点能够成功获取锁,其他节点在此期间被阻塞等待或获取失败。
  2. 唯一性:同一时刻只能有一个节点持有特定的锁。
  3. 避免死锁:分布式锁需要考虑如何避免死锁情况的发生,例如设置适当的超时时间。
  4. 高可用性:分布式锁需要保证在节点故障或网络分区等异常情况下仍能正常工作。
  5. 性能考量:设计高效的分布式锁算法,尽量减少对系统性能的影响。
    常见的实现分布式锁的方式包括使用基于内存的锁服务(如Redis、ZooKeeper)、基于数据库的锁、基于分布式协调服务的锁(如etcd)等。每种方式都有其适用的场景和特点,需要根据具体需求和系统架构选择合适的方式。
    总的来说,分布式锁在分布式系统中扮演着重要的角色,能够帮助解决多节点并发访问共享资源时可能出现的各种问题,确保系统的正确性和稳定性。

分布式锁实现方式

分布式锁三种实现方式
1、基于数据库实现分布式锁;
2、基于缓存(Redis等)实现分布式锁;
3、基于Zookeeper实现分布式锁。
从性能角度(从高到低)来看:缓存方式>Zookeeper方式>=数据库方式

分布式锁应该具备哪些条件

在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:
1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
2、高可用的获取锁与释放锁;
3、高性能的获取锁与释放锁;
4、具备可重入特性;
5、具备锁失效机制,防止死锁;
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

布式锁通常应用场景

秒杀下单、抢红包 接口幂等性
多线程对同一资源的竞争,需要用到锁,例如Java自带的Synchronized、ReentrantLock。但只能用于单机系统中,如果涉及到分布式环境(多机器)的资源竞争,则需要分布式锁。
分布式锁的主要作用:保证数据的正确性:
比如:秒杀的时候防止商品超卖,表单重复提交,接口幂等性,避免重复处理数据:
比如:调度任务在多台机器重复执行,缓存过期所有请求都去加载数据库。
秒杀下单、抢红包等等业务场

  1. 数据库并发控制:在分布式数据库中,多个节点同时对同一个表进行操作时,可能会出现并发冲突的问题。使用分布式锁可以确保同一时刻只有一个节点能够对该表进行操作,以避免并发冲突。
  2. 分布式缓存:在分布式缓存中,多个节点同时对同一个缓存进行操作时,可能会出现数据不一致的问题。使用分布式锁可以确保同一时刻只有一个节点能够对该缓存进行操作,以避免数据不一致的问题。
  3. 分布式任务调度:在分布式任务调度中,多个节点可能同时执行同一个任务,使用分布式锁可以确保同一时刻只有一个节点能够执行该任务,以避免重复执行的问题。

zk分布式锁和redis分布式锁两者对比区别

zookeeper可以基于有序节点再结合Watch机制实现互斥和唤醒,强一致性
Redisson分布式锁底层基于lua+hash数据结构实现的可重入锁,也内置了一个Watch Dog机制来对key做续期
那么基于 CP 模型又能实现分布式锁特性的组件,我认为可以选择 Zookeeper或者 etcd,在数据一致性方面,zookeeper 用到了 zab 协议来保证数据的一致性,etcd 用到了 raft 算法来保证数据一致性。在锁的互斥方面,zookeeper 可以基于有序节点再结合 Watch 机制实现互斥和唤醒,etcd 可以基于 Prefix 机制和 Watch 实现互斥和唤醒。
对于 redis 的分布式锁而言,它有以下缺点:
它获取锁的方式简单粗暴,如果获取不到锁,会不断尝试获取锁,比较消耗性能。
Redis 是 AP 模型,在集群模式中由于数据的一致性会导致锁出现问题,即便使用 Redlock 算法来实现,在某些复杂场景下,也无法保证其实现 100%的可靠性
不过在实际开发中使用 Redis 实现分布式锁还是比较常见,而且大部分场情况下不会遇到”极端复杂的场景“ 更重要的是 Redis 性能很高,在高并发场景中比较合适。
对于 zk 分布式锁而言:
zookeeper 天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。
如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。
如果要在两者之间做选择,就我个人而言的话,比较推崇 ZK 实现的锁,因为对于分布式锁而言,它应该符合 CP 模型,但是 Redis 是 AP 模型,所以在这个点上,Zookeeper 会更加合适。
当前主流的方案有两种:
1)Redis 的 set 加锁+lua 脚本解锁方案,至于是不是用守护线程续命可以结合自己的场景去决定,个人建议还是可以使用的
2)Zookeeper 方案
通常情况下,对于数据的安全性要求没那么高的,可以采用 Redis 的方案,对数据安全性要求比较高的可以采用Zookeeper 的方案。
Redis 可以使用 SetNX 这个指令来实现分布式锁
Zookeeper 可以基于同一级节点的唯一性或者有序节点的特性来实现分布式锁
由于 Redis 的读写性能要比Zookeeper 更好,在高并发场景中,Zookeeper 实现分布式锁会存在性能瓶颈
所以我认为 Redis 比 Zookeeper 更好
1.Redis是基于它的命令执行是单线程的这个特性,所以在获取数据的时候不会有并发问题来去做分布式锁的,其中Redisson客户端,对分布式锁进行了很好的包装,redisson分布式锁 基于lua+hash来实现加锁逻辑,同时利用Redis的发布与订阅以及seamphore来实现锁的等待。并且添加了锁续期等功能。
2.zk的分布式锁,主要是基于zk的临时有序节点+watch机制来实现的,每个线程去尝试加锁都会创建一个临时有序节点,然后拿锁的时候,判断是否是最小的那几个节点,如果不是,就线程安全的去监听上一个节点。然后进行wait。
1.因为Zk是基于临时有序节点去获取锁,所以每个线程去抢占锁,都会去创建一个临时节点,当抢占锁的线程比较多的时候,对ZK集群的压力会比较大。而Redis则是通过判断key是否存在来获取锁。
2.Redis属于cap里面的ap,优先去保证高可用,所以在某些场景下,比如主从切换,可能导致数据丢失,导致锁丢失,而zk则是使用zap协议优先保证数据一致性,所以安全性方面,Redis要比ZK低,但是也有像联锁的方式去提升可靠性。
3.性能方面,Redis基于内存操作,所以性能比zk要高。
当然有可能存在锁过期了,但是业务逻辑还没执行完的情况。所以这种情况,可以写一个定时任务对指定的key进行续期。Redisson这个开源组件,就提供了分布式锁的封装实现,并且也内置了一个Watch Dog机制来对key做续期。
我认为Redis里面这种分布式锁设计已经能够解决99%的问题了,当然如果在Redis搭建了高可用集群的情况下出现主从切换导致key失效,这个问题也有可能造成多个线程抢占到同一个锁资源的情况,所以Redis官方也提供了一个RedLock的解决办法,但是实现会相对复杂一些。
在我看来,分布式锁应该是一个CP模型,而Redis是一个AP模型,所以在集群架构下由于数据的一致性问题导致极端情况下出现多个线程抢占到锁的情况很难避免。
那么基于CP模型又能实现分布式锁特性的组件,我认为可以选择Zookeeper或者etcd,
在数据一致性方面,zookeeper用到了zab协议来保证数据的一致性,etcd用到了raft算法来保证数据一致性。
在锁的互斥方面,zookeeper可以基于有序节点再结合Watch机制实现互斥和唤醒,etcd可以基于Prefix机制和Watch实现互斥和唤醒。
建议:
如果注重数据的准确性,数据不允许有一点错误:可以用zookeeper分布式锁解决,可以保证绝对不出问题,但是性能比redis差。
如果能容忍redis宕机问题导致reddsion分布式锁出现的锁失效问题,从而可能会导致数据出现问题,则可以还是使用redis。
如果使用“看门狗生效红锁方式”,则只需要确保系统时钟不要出现大的跳跃就可以保证零失误,极端条件下出现脏数据的话,就按照自己的系统的情况去处理脏数据吧!

关系型数据库做分布式锁

关系型数据库,可以使用唯一约束来实现锁的排他性,
如果要针对某个方法加锁,就可以创建一个表包含方法名称字段,并且把方法名设置成唯一的约束。
那抢占锁的逻辑就是:往表里面插入一条数据,如果已经有其他的线程获得了某个方法的锁,那这个时候插入数据会失败,从而保证了互斥性。这种方式虽然简单啊,但是要实现比较完整的分布式锁,还需要考虑重入性、锁失效机制、没抢占到锁的线程要实现阻塞等,就会比较麻烦。
关系型数据库作为实现分布式锁的一种方式,通常需要考虑以下几个方面:

  1. 表结构设计:在关系型数据库中,可以创建一个专门用于存储锁信息的表。这个表可以包含字段如锁名称、持有者标识、过期时间等。通过操作这个表来实现对锁的获取和释放。
  2. 事务支持:关系型数据库通常支持事务机制,可以利用数据库的事务特性来确保对锁的操作是原子性的,从而避免出现竞态条件。
  3. 乐观锁和悲观锁:在关系型数据库中,可以使用乐观锁或者悲观锁来实现分布式锁。乐观锁通常通过版本号或时间戳来实现,而悲观锁则是在操作之前先对记录加锁,确保独占性。
  4. 超时处理:可以通过设置锁的过期时间来避免锁被永久占用,即使出现异常情况也能自动释放锁,避免死锁问题。
  5. 分布式环境下的协调:在分布式环境中,需要考虑多个节点之间的协调与同步。可以通过应用程序的逻辑来控制各个节点对数据库的访问,确保只有一个客户端能够成功获取锁。
  6. 性能考量:在使用关系型数据库作为分布式锁的存储介质时,要注意数据库性能是否能够满足需求。频繁地对数据库进行加锁、解锁操作可能会对数据库性能造成影响。
    总的来说,使用关系型数据库作为分布式锁的存储介质需要综合考虑数据一致性、性能、可靠性等方面的因素,合理设计表结构和锁管理机制,确保系统能够正确、高效地实现分布式锁的功能。

使用分布式锁的目的

是为了保证同一时间只有一个 JVM 进程可以对共享资源进行操作。根据锁的用途可以细分为以下两类:
允许多个客户端操作共享资源,我们称为共享锁
这种锁的一般是对共享资源具有幂等性操作的场景,主要是为了避免重复操作共享资源频繁加锁带来的性能开销。
只允许一个客户端操作共享资源,我们成为排他锁
这种锁一般是用在对共享资源操作具有非幂等性操作的场景,也就是需要保证在同一时刻只有一个进程或者线程能够访问这个共享资源。
分布式锁,是一种跨进程的跨机器节点的互斥锁,它可以用来保证多机器节点对于共享资源访问的排他性。
我觉得分布式锁和线程锁本质上是一样的,线程锁的生命周期是单进程多线程,分布式锁的声明周期是多进程多机器节点。在本质上,他们都需要满足锁的几个重要特性:
排他性,也就是说,同一时刻只能有一个节点去访问共享资源。
可重入性,允许一个已经获得锁的进程,在没有释放锁之前再次重新获得锁。锁的获取、释放的方法锁的失效机制,避免死锁的问题所以,我认为,只要能够满足这些特性的技术组件都能够实现分布式锁。

你了解业界哪些大公司的分布式锁框架

1.Google:Chubby
Chubby 是一套分布式协调系统,内部使用 Paxos 协调 Master 与 Replicas。
Chubby lock service 被应用在 GFS, BigTable 等项目中,其首要设计目标是高可靠性,
而不是高性能。 Chubby 被作为粗粒度锁使用,例如被用于选主。持有锁的时间跨度一般为小时或天,而 不是秒级。
Chubby 对外提供类似于文件系统的 API,在 Chubby 创建文件路径即加锁操作。
Chubby 使用 Delay 和 SequenceNumber 来优化锁机制。Delay 保证客户端异常释放锁
时,Chubby 仍认为该客户端一直持有锁。Sequence number 指锁的持有者向 Chubby 服
务端请求一个序号(包括几个属性),然后之后在需要使用锁的时候将该序号一并发给
Chubby 服务器,服务端检查序号的合法性,包括 number 是否有效等。
2. 京东 SharkLock
SharkLock 是基于 Redis 实现的分布式锁。锁的排他性由 SETNX 原语实现,使用
timeout 与续租机制实现锁的强制释放。
3. 蚂蚁金服 SOFAJRaft-RheaKV 分布式锁
RheaKV 是基于 SOFAJRaft 和 RocksDB 实现的嵌入式、分布式、高可用、强一致的 KV
存储类库。
RheaKV 对外提供 lock 接口,为了优化数据的读写,按不同的存储类型,提供不同的锁
特性。RheaKV 提供 wathcdog 调度器来控制锁的自动续租机制,避免锁在任务完成前提前
释放,和锁永不释放造成死锁。
4. Netflix: Curator
Curator 是 ZooKeeper 的客户端封装,其分布式锁的实现完全由 ZooKeeper 完成。
在 ZooKeeper 创建 EPHEMERAL_SEQUENTIAL 节点视为加锁,节点的 EPHEMERAL 特
性保证了锁持有者与 ZooKeeper 断开时强制释放锁;节点的 SEQUENTIAL 特性避免了加锁
较多时的惊群效应。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

思静语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值