下单实现分布式锁

最近一直在优化下单部分,今天优化了一下redis的加锁方式。

为了防止每个下单用户重复点击而造成二次提交,在下单的时候会给每个用户加一把锁。

加锁代码实现:

1
2
$result = redis()->set($key, Context::get('request_id'), ['nx', 'ex' => 10])
return $result;

这个地方使用了nx属性,当不存在的时候才会设置,如果存在的话,就会设置失败。

设置的值为一个请求的track_id,整个请求的生命周期这个track_id都会是一样的。后面会说设置成追踪id的作用。

这个地方不可以先setnx值,然后在expire对应的key,因为这是两条 Redis 命令,不具有原子性,如果在 setnx 之后程序挂了,会使得锁没有设置过期时间,这样就会发生死锁定.

解锁代码实现:

1
2
$lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
$result = redis()->eval($lua, [$key, Context::get('request_id')], 1);

这个地方使用了lua脚本去处理解锁问题,为什么要用lua,因为要保证原子性,先判断获取的值和设置的值是否一样,然后在删除。

这地方不能简单的使用del直接去删除,因为,有可能你删除的键是别人的键,或者不同客户端生成的锁,你也给删了。这就是为什么使用请求的track_id的原因。比如说,你上个请求处理的时间太长了,锁的时间到了,自动释放了,如果不加track_id,可能将下一个请求的锁给释放掉了。


下单实现分布式锁
https://randzz.cn/953604617300/下单实现分布式锁/
作者
Ezreal Rao
发布于
2020年11月9日
许可协议