分布式应用进行逻辑处理时经常会遇到并发问题。如下图所示,一个操作要修改用户的状态。修改状态需要先读出用户的状态,在内存里进行修改,改完了在存回去。
如果这样的操作同时进行,就会出现并发问题,因为“读取”和“保存状态”这两个操作不是原子操作。这个时候就要使用到分布式锁来限制程序的并发执行。
分布式锁本质上要实现的目标就是在redis里面占一个“坑”,当别的进程也要来占坑时,发现那里已经有一个“大萝卜”了,就只好放弃或者稍后再试。
占坑一般使用setnx指令,只允许被一个客户端占坑。先来先占,用完了,在调用del指令释放“坑”。
1、死锁问题
如果逻辑执行到中间出现了异常,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放。
于是我们想到使用expire指令设置过期时间,但是setnx指令和expire指令是两条指令而不是原子指令,有可能出现setnx指令执行但是expire指令不执行的风险,那该怎么解决这个问题呢?
redis 2.6版本开始支持Lua脚本,可以通过Lua脚本执行setnx指令和expire指令来保证原子性。
redis 2.8版本中,作者加入了set指令的扩展参数,使得setnx和expire指令可以一起执行。
2、超时问题
redis的分布式锁不能解决超时问题,稍微安全点的做法就是编写个定时脚本,在快要失效的时候延长失效时间,但是也要注意执行的次数,防止死锁的发生。
3、sentinel集群中锁失效问题
使用redlock算法。
网友评论