分布式同步锁

        在多线程情况下,对共享资源保护独占,需要互斥锁。在微服务情况下,多进程并发访问某个资源时,就需要分布式锁来完成。分布式锁的常用的解决方案是通过 redis 来实现,虽然还有基于关系型数据库、ZooKeeper、Etcd 等的解决方案,但由于 redis 使用最为普遍和完善,并提供 redisson 封装了 redis 分布式锁的框架库,使得分布式锁使用起来极其简单方便,就像使用本地的线程锁,几乎没有使用门槛。

        尽量避免使用分布式锁。使用互斥锁要慎重,特别是分布式锁,锁不仅让性能下降, 而且使用不好很容易出现严重危害的死锁,并且难以定位解决。要避免使用锁,在并行时尽量避免资源共享,如果从业务和架构无法避免,再看看在使用该资源时能否实现原子操作或转换为串行。通过使用函数式编程,避免像面向对象方式在并发时避免状态共享,从而避免使用锁。类似的,通过把各个进程的资源复制,避免共享,从而避免使用分布式锁。如果在性能够,足够稳定,不需要高可用的情况下,避免该微服务只部署一个实例,从而也避免了资源共享。 又或者对共享的资源采用原子性操作,比如,购物系统下单时,该商品有货下能正常下单,下单后该商品库存减一,可以该商品的库存用 redis 维护,并在下单时采用 redis lua 脚本这样原子性操作检测是否满足货品数量,满足时减少该库存,不满足时返回失败,从而避免分布式锁。还有通过消息队列方式,把并发消息放入消息队列,消费端再从消息队列串行处理,从而避免并行占用资源,从而不使用分布式锁。避免使用分布式锁各种方案利弊需要仔细权衡,不能为了避免使用分布式锁的成本远高于使用分布式锁,则得不偿失了。

        redis实现分布式锁的原理。 redission 库对 redis 分布式锁实现了非常好的封装,虽然不用了解原理也能够轻松使用,但了解原理后使用起来更加得心应手。redis 的命令是原子操作,加锁时通过命令 ET key value NX EX  来完成不存在 key 的添加,并通过 EX 可以设置锁的过期时间, key 值为锁名,如果该 key 存在,则该命令执行失败,表示该锁被其它进程获得,自己加锁失败。释放锁时,del key (删除锁名  ) 就行。

        上面存在误释放其他进程锁的问题, 如果当第一个进程加带有时效的锁,由于业务流程长, redis 到期后自动删除 key,从而释放了第一个进程的锁。此时,第二个进程可以获得该锁,在 redis 添加了该 key,第一个进程现在把业务执行完,执行释放锁时,就会误把第二个进程加的锁释放掉,从而第二个进程没有锁住资源。为了避免这个误释放锁的问题,redis 设置 key 的 value 时 设置一个随机值,不同的进程的 value 值不同,当一个进程释放锁时,需要 key 和 value 和该进程加锁时值都相同,才能删除该 key,从而正确地释放锁,否则,value 不一致,表示该锁不是这个进程拥有的,无法释放。获取和检查 key  value 和删除 key 在 redis 是两个命令,为了保持原子性,需要 lua 脚本来实现这个逻辑,从而保证原子性。

        还有一种情况,当第一个进程加的锁没有设置超期时间,随后第一个进程崩溃,导致该锁永远无法释放,从而形成死锁。为了解决该问题,在 redisson 库中有个看门狗机制,在设置没有过期时间锁时,redisson 加锁会默认设置 30 秒过去,然后看门狗不断地续期,直至该进程主动释放锁,如果在释放锁之前该进程崩溃了,那么看门狗也就跟随崩溃无法给该锁自动续期,从而最多 30 秒后 redis 自动释放该锁,避免了死锁情况的出现。

         还有一种没有锁住的情况,当 redis 采用主从模式,当一个进程刚加锁,redis 的 master 崩溃,还没来得及同步到 slave redis,而从 redis 被选举成 master,此时刚才那个进程加锁实际是失败的。为了解决这个问题,需要多个 master,一般推荐 5 个, 当加锁时是对所有 master redis 加锁,结果有超过半数获取锁成功,才代表该进程加锁成功,在释放锁时,也得超过半数释放锁成功才代表该进程成功释放完锁。通过红锁机制,即使偶尔某个 master redis 出现了问题,也不会影响锁的结果,当然还得加上看门狗防止死锁。

        以上 redis 分布式锁的主要原理和异常场景的处理,redisson 处理完善程度比上面的更多,它在分布式锁和同步上实现了可重入锁(ReentrantLock)、公平锁(FairLock)、联锁(MultiLock)、红锁(RedLock)、读写锁(ReadWriteLock)、信号量(Semaphore)、闭锁(CountDownLatch)、栅栏(CyclicBarrier),非常完整,可以满足绝大多数使用场景。

        总结,不要盲目使用分布式锁,多考虑一些其他解决方案,确定分布式锁是最佳解决方案时,就使用 redis 的 redisson 库。根据业务对可靠性要求,确定 redis 的部署方式,慎重使用红锁,它的复杂度和部署成本都比较高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值