假设我们目前在做一个抽奖活动,我们使用redis进行计数:
![](https://img.haomeiwen.com/i8895870/a603126435243854.png)
以下是我们的代码实现:
function v1() {
$amountLimit = 100;
$keyName = getKeyName('v1');
$redis = getRedisClient();
$incrAmount = 1;
if (!$redis->exists($keyName)) {
$redis->set($keyName, 95);
}
$currAmount = $redis->get($keyName);
if ($currentAmount + $incrAmount > $amountLimit) {
printf("Bad luck");
return;
}
printf("Good luck");
}
并发场景: 客户端A,B同时访问数量控制器,会存在什么问题?
- 问题 1:假定A,B同时读到key不存在,然后由于种种原因,代码运行速度不一致,出现 A 先执行了
incrby
,进行加1操作,然而 B 此时才进行初始化,执行set
操作, 覆盖之前的数据。这样一来则会出现数据不一致的现象。 - 问题 2:假定限量100,A,B 同时读到当前数量为99,A ,B先执行
incrby
后,总量为101,超卖了。
如何解决并发问题
-
对于问题1,我们使用
setnx
命令进行解决。当 A 先执行了incrby
,进行加1操作,然后 B 在次进行初始化,执行setnx
操作时,该命令只在键 key 不存在的情况下, 将键 key 的值设置为 value 。若键 key 已经存在, 则setnx
命令不做任何动作。因此不会出现数据不一致的情况。 -
对于问题2,我们先执行
incrby
命令,通过返回值进行判断是否超限。因为incrby
命令是原子操作,线程安全。并发情况下,串行执行。即不会出现超买的情况。
![](https://img.haomeiwen.com/i8895870/25b1baa49d42bfce.png)
function v2() {
$amountLimit = 100;
$keyName = getKeyName('v1');
$redis = getRedisClient();
$incrAmount = 1;
if (!$redis->exists($keyName)) {
$redis->setnx($keyName, 95);
}
$currAmount = $redis->get($keyName);
if ($redis->incrby($keyName, $incrAmount) > $amountLimit) {
printf("Bad luck");
return;
}
printf("Good luck");
}
网友评论