前言
在项目中我们经常有需要使用分布式锁的场景,而Redis是实现分布式锁最常见的一种方式,这篇文章主要是使用Go+Redis实现互斥锁和红锁。
下面的代码使用go-redis客户端和gofakeit]库。
互斥锁
Redis里有一个设置如果不存在
的命令,我们可以通过这个命令来实现互斥锁功能,在Redis官方文档里面推荐的标准实现方式是SET resource_name my_random_value NX PX 30000
这串命令,其中:
resource_name
表示要锁定的资源NX
表示如果不存在则设置PX 30000
表示过期时间为30000毫秒,也就是30秒my_random_value
这个值在所有的客户端必须是唯一的,所有同一key的锁竞争者这个值都不能一样。
值必须是随机数主要是为了更安全的释放锁,释放锁的时候使用脚本告诉Redis:只有key存在并且存储的值和我指定的值一样才能告诉我删除成功,避免错误释放别的竞争者的锁。
由于涉及到两个操作,因此我们需要通过Lua脚本保证操作的原子性:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
举个不用Lua脚本的例子:客户端A取得资源锁,但是紧接着被一个其他操作阻塞了,当客户端A运行完毕其他操作后要释放锁时,原来的锁早已超时并且被Redis自动释放,并且在这期间资源锁又被客户端B再次获取到。
因为判断和删除是两个操作,所以有可能A刚判断完锁就过期自动释放了,然后B就获取到了锁,然后A又调用了Del,导致把B的锁给释放了。
TryLock和Unlock实现
TryLock
其实就是使用SET resource_name my_random_value NX PX 30000
加锁,这里使用UUID
作为随机值,并且在加锁成功时把随机值返回,这个随机值会在Unlock
时使用;
Unlock</