实现一
首先考虑使用 setnx 实现键不在加入,键在不加的单对象持有锁功能;
接着考虑程序失败可能导致的锁一直不释放问题,搭配 expire 来实现过期自动删除;
又因为这两个命令执行中间可能出错,不是原子性的,可以使用 set 加上 nx、ex/px 参数来实现原子操作;
实现二
如果程序执行太久,而锁过期时间太短,会导致键被删除了,但实际上程序还需要用到该锁,这时可以在持有锁时加入一个随机数,在删除时先判断是否是对应的随机数再进行删除,又因为多条命令的原子性问题,可以引入 Lua 脚本实现多条命令的原子操作;这样只是相对地安全,如果想再提高保障性,则需要引入可重入锁,在锁键过期时该对象再次持有锁,但这种方式实现起来及其复杂。
实现三
RedLock(Redis Distributed Lock) 也即是 redis 实现的分布式锁,适用于多节点环境下的分布式锁实现,可以有效防止单点故障导致的锁问题。
这里先明确锁过期时间设为 TTL ,客户端获取锁超时时间为 timeout,时钟漂移为 cd。
首先在客户端发出加锁命令之前会先获取时间戳【用于计算超时时间】,之后依次向多个节点发送获取锁的请求,超时会放弃获取该节点锁,进而获取下一节点,待全部节点都跑完一遍之后,统计获取到的锁数量,这里注意锁应该是在有效期内的(即TTL-timeout-cd>0),如果获取到的锁数量大于总数的一半则视为获取成功;如果客户端获取锁失败(获取锁数量少于一半),则需要释放之前获取到的部分节点的锁。当客户端无法获取锁时,应当在随机时间之后再次尝试获取,获取后完成任务应该及时释放,释放时需要向全部节点发送删除命令。
使用 RedLock 可以保证分布式多节点环境下获取锁释放锁的高可靠性,但相对的性能上会较其他加锁方式低一些。