分布式锁是什么

分布式锁是用来解决在分布式场景中,多服务实例并发争抢共享资源时,且该资源同一时刻只能一个访问,就需要通过加锁来让获得锁的应用执行,获取不到锁的服务不执行或者后续执行。

分布式锁的解决方案

Redis分布式锁

Redis分布式锁依赖setnx命令来实现。

具体实现思路:

  • 服务1尝试拿锁的时候,setnx key value返回的是1,表示设置值成功,也就是拿到了锁,顺利执行业务逻辑。
  • 后续服务2尝试拿锁的时候,setnx key value返回的是0,表示设置值失败,也就是拿锁失败。
  • 这样就可以保证只有一个服务执行。

服务1执行完业务逻辑之后呢,应该有一个锁释放的动作,不然key永远存在,不会再有服务能够拿到锁,也就是出现死锁

如何避免死锁

锁释放分为:主动删除和自然消亡。

  • 主动删除就是业务逻辑执行完毕,服务代码主动删除,依赖del key命令来实现。
    需要注意的是,主动删除锁的逻辑最好放到代码块的finally里,可以防止代码执行异常无法释放锁的情况。

但是如果代码服务突然挂了呢,主动删除是解决不了的。

  • 自然消亡就是通过Redis提供的key过期机制,依赖set key value ex seconds nx命令来实现。
    需要注意的是,过期时间的设置问题。

如果设置过短,可能业务还没有执行完;如果设置过长,可能就会造成卡顿等问题。

既然都有些问题,为了增加健壮性,那就采用两种结合的方式吧:代码主动删除key+redis_key自动过期,双道保险来保证。

那还有问题么?有的。

存在一种可能情况是,本服务key被其他服务释放掉的情况:
服务1处理业务很慢,大于key的过期时间,这时候锁就自然消亡,锁后面可以被服务2拿到,存在服务2执行业务过程中,key被服务1主动删除的情况。

针对这个情况,也好解决,就是set-key的时候,在value里加上当前服务的标识,可以是随机数,在主动删除key的时候,做一个get-key的value校验。

那还有问题么?还有的。

仔细分析流程后我们发现,判断锁是否属于当前服务和释放锁两个步骤并不是原子操作
假如在执行删除锁的动作之前,系统卡顿了几秒钟,恰好在这几秒钟内,key自动过期了,
服务2就顺利获取到锁开始执行自己的逻辑了,此时,服务1卡顿恢复了,开始继续执行删除锁的动作,那么此时删除的还是服务2的锁。

如下图所示:

redis_lock_1.png

终极解决-Lua脚本

Lua是一门胶水语言,它支持原子性操作,Redis会将整个Lua脚本作为一个整体执行,中间不会被其他请求插入,因此Redis执行Lua脚本是一个原子操作。

在上面的流程中,我们把get-key、判断value是否属于当前服务、del-key这三步写到Lua脚本中,使它们变成一个整体交个Redis执行,改造后流程如下:

redis_lock_1.png

Lua脚本示例:

local key = KEYS[1]
local value = KEYS[2]

if redis.call('get',key) == value
then
    return redis.call('del',key)
else
    return false   
end

当然,上面加锁的过程,我们也可以通过Lua脚本实现。

expire的时间适用性

  • 提供时间冗余,时间设置足够的长,优先保证业务完毕,但是不推荐使用。
  • 用Redisson的Watch Dog机制解决,简单来说就是,它是一个守护线程,定时去检查key的时效,如果业务还没有执行完&key快要过期了,就进行key的续期。

总结

针对上面redis分布式锁的解决方案,单机Redis节点是没什么问题的;
但是企业开发时,往往都会有'Redis集群',主从同步。主节点挂了,从节点还没有同步完的时候,key丢了,就会有问题。

一般Redis分布式锁就够用了,所以其他的解决方案我们简单分析一下。

Zookeeper实现分布式锁

客户端1和客户端2都创建临时节点 /lock

假设1创建成功,资源操作,锁释放。
没有过期时间一说。只要保证1拿到锁之后,与zk的连接保持不断(定时心跳机制),就一直拿到锁,如果断了,就会自动删除节点,释放锁。

zk不用考虑过期时间,可以实现实现分布式锁,但是单独搞一个zk,我觉得成本太大。

RedLock解决方案


5台独立的实例 具有容错机制

获取流程:
1.客户端获取一个当前时间戳1
2.客户端向5台客户端发起加锁请求,并设置毫秒级的超时时间。如果某一个实例失败,那么就继续下一个加锁。最后>=3个加锁成功,再获取一个时间戳2,如果时间戳2-时间戳1<锁过期时间,就成功,可以执行业务代码。
 否则失败,就去解锁。

更多参考:https://blog.csdn.net/lisheng19870305/article/details/122464924

Last modification:January 28, 2023
如果觉得我的文章对你有用,请随意赞赏