• 作者:老汪软件技巧
  • 发表时间:2024-09-23 21:03
  • 浏览量:

食用建议:这篇文章主要是理解并在自己心中对分布式锁有个框架,使得在工作中能从容面对,具体实现的代码并没有阐述,网上其实有很多,如果需要作者整理的话,点赞超过10个就整出来巨详细的步骤!

1. 为什么需要分布式锁?

首先我们需要明白为什么需要加锁,再并发环境下为保证数据一致性,则需要加锁;比如库存管理,当多个线程操作同一条库存记录的时候,需要加锁来保证互斥;我们可以通过ReentrantLock、synchronized来是实现;

加分布式锁是因为再分布式集群部署的情况下,并发指的不是多个线程,二是多个进程或多个服务器;如果只是加线程锁只能保证一个JVM下的线程互斥,而其他的JVM还是可以进行访问的,从而造成数据不一致!

分布式锁必须要具备的特性:

2. 分布式锁那些实现方案?2.1. 实现方案及原理2.1.1. 数据库实现分布式锁增加锁表(主要记录那个机器那个服务那个方法,获取锁时,则进行插入,如果插入成功则加锁成功,否则失败;释放锁删除记录即可)乐观锁(条件,比如库存>0的时候才修改;版本号,如果版本号一直则修改,修改后版本号+1)悲观锁(select for update,正常情况下是行锁,如果没有正确使用,比如没有索引或没有记录或隔离级别是串行化则会升级成表锁!)2.1.2. Zookeeper实现分布式锁

主要通过znode节点和watch机制实现的;

znode节点有四种:

持久节点:一旦创建,永久存在zookeeper中,除非手动删除持久有序节点:持久节点增加序号,并且是有序的;临时节点:节点创建后,如果服务重启或宕机,则被删除;临时有序节点:临时节点增加序号,并且是有序的;

_一文彻底搞懂分布式锁(强烈建议打开看看!)_一文彻底搞懂分布式锁(强烈建议打开看看!)

watch机制: 监听节点变更,比如B节点监听A节点,当A节点状态发生变更或删除,则B节点就会收到通知!

实现流程:

当首次对共享资源访问的时候,zookeeper会创建一个持久节点作为父节点当有其他应用进程访问的访问的时候,会在父节点后面依次创建临时有序节点如果临时有序节点是最小的,则可以获取到锁没有获取到锁的进程会对前一个节点进行监控,一旦就会依次唤醒

所以可以理解为zookeeper是公平锁,但是可以是实现非公平锁,比如只创建一个临时有序节点,其他进程进行争抢!

2.1.3. Redis实现分布式锁setnx(如果key不存在则设置成功) + expire(设置过期时间,避免死锁) + delete (释放锁)setnx + expire可以使用一条命令来实现保证原子性;加锁时使用requestId避免其他线程释放锁,比如A获取锁后因业务逻辑没有执行完就到了过期时间释放了锁,B获取到锁后刚开始执行,A执行结束就把锁释放了,增加requestId后,可以先查询在判断是否可以释放,因为分了两步所以需要使用lua脚本保证原子性;过期时间设置问题,如果A没有执行结束就释放锁,如果B还是和A执行相同的业务逻辑就会造成重复执行,可以在创建锁的时候增加守护线程来通过定时任务增加过期时间,释放的时候删除守护线程;使用三方类库Redisson上面的requestId以及过期时间问题,Redisson早就解决了,requestId在lua脚本中有判断、过期时间是通过看门狗机制实现的加解锁实现原理,请看图:

RedLock(红锁)解决集群部署时,如果主节点挂了,但是没有把锁信息同步给从节点而出现问题原理:为每个节点加锁,如果加锁的节点大于一般则获取锁成功,释放锁也是同理在业界很有争议,redison的作者Antirez和Martin(分布式大佬)battle,Martin说并没有保证并发安全,分布式环境可能会出现线程暂停,比如A在加锁后进程暂停了,超过过期时间进行释放,B获取锁执行中,A进程开始执行就会认为获取到了锁会继续执行从而出现问题;而Antirez会说会检测到从而停止执行!2.2. 具体区别以及适用场景数据库实现分布式锁性能比较差,有较大的代码改动;Redis中更多的采用的是Redisson方案,简单,并且性能是三种方案中最好的,可以抗住高并发的加解锁,但是在集群部署的收也会偶尔出现数据不正确的情况;Zookeeper保证强一致性,如果加解锁频繁Zookeepre服务器压力会很大

具体选择那个根据实际开发环境选择,比如我们系统已经使用了Redis做缓存,也能容忍极少数情况下数据不正确就选择了Redisson;如果你们系统已经使用了Zookeeper并且数据要求非常严格,就是用Zookpeer实现分布式锁!