问题
原子性问题
指令 setnx 和 expire 组合在一起的原子指令,是分布式锁的奥义所在。
> set lock:codehole true ex 5 nx
OK
... do something critical ...
> del lock:codehole
超时问题
随机数
通过随机数,释放锁时先匹配随机数是否一致,然后再删除 key,
确保这个锁是过期了被服务器自动释放
tag = random.nextint() # 随机数
if redis.set(key, tag, nx=True, ex=5):
do_something()
redis.delifequals(key, tag) # 假想的 delifequals 指令
Lua脚本
匹配 value 和删除 key 不是一个原子操作,
因为 Lua 脚本可以保证连续多个指令的原子性执行。
# delifequals
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
B的锁被A给释放了
Redis实现锁的原理在于 SETNX命令。
当 key不存在时将 key的值设为 value ,返回值为 1;
若给定的 key 已经存在,则 SETNX不做任何动作,返回值为 0 。
SETNX key value
场景:A、B两个线程来尝试给key myLock加锁,A线程先拿到锁(假如锁3秒后过期),B线程就在等待尝试获取锁,到这一点毛病没有。
那如果此时业务逻辑比较耗时,执行时间已经超过redis锁过期时间,这时A线程的锁自动释放(删除key),B线程检测到myLock这个key不存在,执行 SETNX命令也拿到了锁。
但,此时A线程执行完业务逻辑之后,还是会去释放锁(删除key),这就导致B线程的锁被A线程给释放了。
为避免上边的情况,一般我们在每个线程加锁时要带上自己独有的value值来标识,只释放指定value的key,否则就会出现释放锁混乱的场景。
redis锁的过期时间能够自动续期
为了解决这个问题我们使用redis客户端redisson,redisson很好的解决了redis在分布式环境下的一些棘手问题,它的宗旨就是让使用者减少对Redis的关注,将更多精力用在处理业务逻辑上。
redisson对分布式锁做了很好封装,只需调用API即可。
RLock lock = redissonClient.getLock(“stockLock”);
复制代码redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗”.