1.分布式锁有哪些实现方式?
分布式锁:在多系统中,对同一共享资源保证最终一致性(也就是要保证cp,一致性和分区容错)。 在单机中,可以通过锁来保证线程对共享资源的安全,在多机器上可以通过分布式锁来保证对共享资源的安全。
用redis和zookeeper来实现分布式锁。
2.使用redis如何设计分布式锁
普通的方式实现分布式锁
在setnx命令创建一个key,这算加锁。
SET resource_name my_random_value NX PX 30000注:1.
NX
:表示只有key
不存在的时候才会设置成功。(如果此时 redis 中存在这个 key,那么设置失败,返回nil
)2.
PX 30000
:意思是 30s 后锁自动释放。别人创建的时候如果发现已经有了就不能加锁了。
释放锁就是删除key,一般使用lua脚本,判断value一样才删除(删除锁的时候,找到key对应的value,跟自己传过去的value做比较,一样才删除)
用random_value是因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 lua
脚本来释放锁。
问题:如果是普通的 redis 单实例,那就是单点故障。如果 redis 普通主从,那 redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。
redlock算法
在多个实例中,多个master节点上创建锁,要求是n/2+1(如5 个节点就要求是 3 个节点建立锁);如果建立锁的时间小于超时时间就算建立成功了,如果失败就删除建立过的锁。
目的:避免单节点故障。
3.使用zk实现分布式锁
创建临时节点
两个机器节点同时在zookeeper上创建同一个临时节点,如果某个节点创建成功,就获取到锁,这时候其他节点就会创建失败,只能注册监听器来监听这个锁;释放锁就是删除这个临时节点,一旦释放锁监听器就会通知其他的节点,其他节点就可以加锁了。
创建临时顺序节点
如果有一把锁,被多个人给竞争,此时多个人会排队(创建的最小节点获得锁),第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听排在自己前面的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 zookeeper 给通知,一旦被通知了之后,就 ok 了,自己就获取到了锁,就可以执行代码了。
4.redis和zk分布式锁的对比
- redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
- zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
注:如果是 redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。