假设我们目前在做一个抽奖活动,我们使用redis进行计数:
流程图.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命令是原子操作,线程安全。并发情况下,串行执行。即不会出现超买的情况。 
流程图.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");
 }













网友评论