zl程序教程

您现在的位置是:首页 >  其他

当前栏目

分布式锁原理没搞懂,错失大厂offer

分布式分布式原理 大厂 Offer 搞懂 错失
2023-06-13 09:15:32 时间

应用程序可支持多节点,集群运行,多个节点分别在不同的机器运行,访问共享资源,为了防止并发问题,数据不一致,所以需要才用分布式锁来保证数据的安全。今天我们来讨论一下使用redis怎么实现分布式锁。

一.简单设置key来加锁

**setnx : **对应的key为空,就设置值,并返回1 ;对应的key非空,不设置值,并返回0 举例说明: A节点:setnx lock_key 1 ,返回1 ,A节点获取分布式锁成功 B节点:setnx lock_key 1 ,返回0,B节点获取分布式锁失败,可重试不断获取 A节点获取到分布式锁后执行业务逻辑,执行完后,执行 del lock_key 操作,把锁释放。 这个方案是否简单完美呢?简单是很简单,但是并不完美,如果A节点在执行业务逻辑时或者是执行del操作失败了,那锁是不是就没有释放,其他节点就永远没有办法获取锁了。

二. 给key加上超时时间

A节点获取到分布式锁后,通过EXPIER命令给key设置过期时间,但是这样也会有方案一出现的问题,如果A节点给key设置过期时间之前发生了宕机了,因为获取锁和给锁设置过期时间不是原子操作导致的。 那redis有没有命令支持设置key的同时设置过期时间呢? 有的:set lock_key 1 ex 10 nx ,代表 key不存在时设置1,并设置过期时间为10s,成功返回OK,否则返回NIL 给key设置了过期时间后,及时A节点没有释放锁的情况,等待过期时间,锁也会被redis当做过期key释放掉。这要求我们设置的过期时间要比执行业务的时间要长。 这个方案还有问题吗?我们想一下,如果A节点执行业务逻辑的时间太长,超过了锁的过期时间,锁就会被释放;B节点会获取到,等待A节点执行完,会释放锁,这时候会把B节点持有的锁释放掉,这个是不正确的,怎么防止这个问题呢?

三. 给key的value设置特定值

A节点设置value的时候,设置成UUID, 解锁的时候,先获取key值,如果相等才解锁,如果不相同就不解锁; 获取key值和解锁可以使用lua脚本实现,redis是支持执行lua脚本的 加锁: set lock_key UUID ex 10 nx 解锁: if redis.call('EXISTS', UUID) == _1 _then redis.call('DEL', UUID) 这个方案可以防止A节点解锁时,把其他节点的锁释放了,现在还有其他问题吗?给key设置的过期时间其实是不好把握的,我们并不确定业务执行所需要的时间,所以还是会出现锁过期了,业务还是执行的情况,这时候就会出现多个节点同时执行共享资源代码的情况,这个怎么解决呢?

四. 给key自动续期

A节点获取到锁后,启动一个线程,定时检查,锁是否还是属于A节点的(如果锁对应的key值等于A节点设置的值),就延长过期时间,定时检查时间要小于过期时间 这样就可以确保及时超过了设置的过期时间,还没执行完业务逻辑,也不会导致锁过期被其他节点获取的情况了。 这个其实是有现成的框架(redisson)已经实现好了,我们直接就可以拿来使用了,大家如果有需求可以直接使用,它底层是通过一个叫看门狗的线程来实现续期的,大家感兴趣也可以去看看源码。

五.其他

到目前为止,大家觉得还会有其他问题吗? 如果redis是集群或者是哨兵部署的话,还是有可能存在多个节点获取到锁的情况 A节点获取到锁,这时候发生了主从切换,A节点的锁在新的主节点还没同步过来,所以还不存在,这时候其他节点就可以获取锁。 这时候需要使用redlock来解决,但是性能很低,如果不是必须要强保证数据的一致性,不推荐使用,使用上面方案就足够了。

今天的分享就到此结束,如果觉得本文不错的还请伙伴们帮忙点赞转发,欢迎持续关注我们!