php使用redis乐观锁秒杀库存遗留问题和解决办法

管理员

1.1测试代码

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$userkey = "sk" . $_GET['product_id'] . 'user';
$kckey = "sk" . $_GET['product_id'] . 'qc';
$uid = rand(100000000, 999999999);

//乐观锁
$redis->watch($kckey);

//获取
$kc = $redis->get($kckey);
if ($kc == null) {
    echo "秒杀还没开始,请等待";
    $redis->close();
    return false;
}

//判断用户是否已经秒杀过了
if ($redis->sIsMember($userkey, $uid)) {
    echo "已经秒杀过了。不可以重复秒杀";
    $redis->close();
    return false;
}


if ((int)$kc <= 0) {
    echo "秒杀已经结束了";
    $redis->close();
    return false;
}

$multi = $redis->multi();
$multi->decr($kckey);
$multi->sAdd($userkey, $uid);
$result = $multi->exec();

if ($result == null || count($results) == 0) {
    echo "秒杀失败了";
    $redis->close();
    return false;
}

echo "成功";
$redis->close();

1.2 开始测试

//连接redis
vagrant@homestead:~$ redis-cli 
//写入库存
set sk1qc 2000

//使用ab压测
ab -n 3000 -c 300 网站地址?product_id=1

//执行
127.0.0.1:6379> get sk1qc
"928"

2.1.使用lua脚本解决乐观锁库存遗留问题

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$uid = rand(100000000, 999999999);

$lua = <<<'LUA'
   local userid = KEYS[1];
   local prodid = KEYS[2];
   local qtkey = "sk"..prodid.."qc";
   local userskey = "sk"..prodid.."user";
   local userExists = redis.call("sismember", userskey, userid);
   if tonumber(userExists) == 1 then
    return 2
   end 
   
   local number = redis.call("get", qtkey);
   if tonumber(number) <= 0 then
    return 0
   else 
     redis.call("decr", qtkey);
     redis.call("sadd", userskey, userid);
   end 
   
   return 1;
LUA;

$s = $redis->eval($lua, [$uid, $_GET['product_id']], 2);
var_dump($s);

2.2 开始测试

//连接redis
vagrant@homestead:~$ redis-cli 
//写入库存
set sk1qc 2000

//使用ab压测
ab -n 3000 -c 300 网站地址?product_id=1

//执行完成后发现库存还有遗留
127.0.0.1:6379> get sk1qc
"0"
0人点赞
PHP
管理员

全部评论 0

推荐阅读 更多精彩内容