在分布式应用中经常出现并发的问题,比如对用户的状态做一个修改 ,那么就涉及到先取出数据再进行修改,但是这样常常会引发问题,因为读和写是两个操作,不是原子性(不可分割的)的操作。当同时进行该操作时往往会引发并发问题。
这时就要用到分布式锁。
在Reids中的分布式锁实际上就相当于一个占坑,当坑上有人的时候别人必须再外面等待,同时只允许一个人占坑。而在Redis中提供了一个setnx的指令来对key进行加锁:
setnx mylock true
...do something... //做一些业务逻辑
del mylock //释放锁
好像这样就完事大吉了。不,这有问题。如果我在做一些业务逻辑的时候出了问题执行不到del指令释放锁的时候岂不是进入了死循环,锁得不到释放,别人得不到锁。
这就尴尬了,但是我们不是有一个设置过期时间的指令吗?对啊 就是expire指令:
setnx mylock true
expire mylock 5 //设置过期时间为5s
...do something... //做一些业务逻辑
del mylock //释放锁
这样不就解决了吗?别高兴的太早了,万一我在setnx和expire之间出了问题呢,那还是一样的啊,问题依旧,依然出现了死锁。那我们可不可以用事务来解决呢?答案是不行的,因为事务是要不都执行要不都不执行,而我们需要的是if/else的操作,expire指令是依赖于setnx指令的结果的,如果setnx没有抢到锁,那么expire是不用执行的。
现在我们来分析一下为什么会出现这样的问题,其实就是因为setnx和expire是两个操作不是原子性的,所以这样是会出现问题的。那么我们可不可以把这两个指令合并一下呢?答案是可以的。Redis为我们提供了set指令:
set mylock true expire 5 nx //setnx和expire的结合
...do something... //做一些业务逻辑
del mylock //释放锁
这个set指令就是分布式锁的奥义所在。
但是上面的操作还是存在问题的,因为设置了过期时间。万一我的业务逻辑执行时长超过过期时间的化,那么我就释放掉了锁,我都没执行完呢就没了,难受啊。这就是超时问题。所以分布式锁还是不要用来去执行时间比较长的任务。
原文地址:https://www.cnblogs.com/lgxblog/p/11102072.html