基础版
直接SETNX lock 1就行。但有个问题A设置完,假设A的业务流程卡了或者直接宕机了。其他业务无法获取到锁。
设置过期时间
SET lock 1 nx ex 3,设置一下过期时间就行。但是还存在一个问题,这个对于过期时间设置需要比较高超的技巧。假设设置的没业务执行时间长,或者业务执行时候网络延迟、GC卡顿了。那么A没等主动释放锁呢,先过期了,B就会获取到锁,结果A这时候执行完了给人家B的锁释放了。这肯定不行
设置owner
SET lock tju nx ex 3,然后释放锁的时候要先比对一下这个锁是不是我的。遇到上面的情况发现不是自己的,那么证明已经过期了,就不释放了。
原子化
其实比对再释放这两操作不是原子的,可能比对发现是自己的,然后DEL时候已经不是自己的了。所以可以把这两步合成一个Lua脚本里,执行脚本的命令是一个原子化操作。
Lua脚本
其实redis也有原生的multi事务来保证原子性,但是假设出错不会中断或回滚,它会继续执行。Lua可以保证事务失败就立刻中断后续执行,但是之前执行了的部分没法挽回,所以Lua只是保证了不被打断的这个原子性
可靠性
其实上面的那个方案已经基本满足大多数场景了。但是可靠性没有保证,就是你得容灾。
主从
主节点寄了,从节点顶上。这个Redis有哨兵模式可以帮忙解决,不用人工介入。
在一定程度上容灾了,但是有问题,数据同步是有延迟的,A在主节点获得锁,这个信息没来得及同步到从节点,主节点就寄了,B在从节点上同样获取了这个锁。
多机部署
这个Redis的Redlock就是这个方式,通过设置奇数个Redis主节点(实际上就是集群模式,每个Redis节点下面还可以用主从再套娃)来容灾。
假设是5个,每次都会向所有5个Redis申请加锁,超过半数返回成功,也就是3个才会获得锁。反之失败,要向所有Redis节点发送释放锁命令(因为可能是获得成功的,但是网络原因让其返回失败了)。使用完后向所有节点发送解锁请求。
这样即使挂了2台机器,还是可以正常运行,给运维提供了处理时间
但总之没有绝对可靠的方式
因为本来网络就不是完全可靠的,关键业务肯定还是要靠幂等兜底的。Redlock的集群模式可以尽可能降低出错概率(但开销比较大)