使用Redis分布式锁的详细方案是什么?
一个很简单的答案就是去使用 Redission 客户端。Redission 中的锁方案就是 Redis 分布式锁的比较完美的详细方案。
那么,Redission 中的锁方案为什么会比较完美呢?
正好,我用 Redis 做分布式锁经验十分丰富,在实际工作中,也探索过许多种使用 Redis 做分布式锁的方案,经过了无数血泪教训。
所以,在谈及 Redission 锁为什么比较完美之前,先给大家看看我曾经使用 Redis 做分布式锁遇到过的问题。
我曾经用 Redis 做分布式锁想去解决一个用户抢优惠券的问题。这个业务需求是这样的:当用户领完一张优惠券后,优惠券的数量必须相应减一,如果优惠券抢光了,就不允许用户再抢了。
在实现时,先从数据库中先读出优惠券的数量进行判断,当优惠券大于 0,就进行允许领取优惠券,然后,再将优惠券数量减一后,写回数据库。
当时由于请求数量比较多,所以,我们使用了三台服务器去做分流。
这时候会出现一个问题:
如果其中一台服务器上的 A 应用获取到了优惠券的数量之后,由于处理相关业务逻辑,未及时更新数据库的优惠券数量;在 A 应用处理业务逻辑的时候,另一台服务器上的 B 应用更新了优惠券数量。那么,等 A 应用去更新数据库中优惠券数量时,就会把 B 应用更新的优惠券数量覆盖掉。
看到这里,可能有人比较奇怪,为什么这里不直接使用 SQL:
update 优惠券表 set 优惠券数量 = 优惠券数量 - 1 where 优惠券id = xxx
原因是这样做,在没有分布式锁协调下,优惠券数量可能直接会出现负数。因为当优惠券数量为 1 的时候,如果两个用户通过两台服务器同时发起抢优惠券的请求,都满足优惠券大于 0 的条件,然后都执行这条 SQL 语句,结果优惠券数量直接变成 -1 了。
还有人说可以用乐观锁,比如使用如下 SQL:
update 优惠券表 set 优惠券数量 = 优惠券数量 - 1 where 优惠券id = xxx and version = xx
这种方式就在一定几率下,很可能出现数据一直更新不上,导致长时间重试的情况。
所以,经过综合考虑,我们就采用了 Redis 分布式锁,通过互斥的方式,以防止多个客户端去同时更新优惠券数量的方案。
当时,我们首先想到的就是使用 Redis 的 setnx 命令,setnx 命令其实就是 set if not exists 的简写。
当 key 设置值成功后,则返回 1,否则就返回 0。所以,这里 setnx 设置成功可以表示成获取到锁,如果失败,则说明已经有锁,可以被视作获取锁失败。
setnx lock true
如果想要释放锁,执行 del 指令,把 key 删除即可。
de