Redis 并发防止超卖


那么,既然“下单减库存”和“付款减库存”都有缺点,我们能否把两者相结合,将两次操作进行前后关联起来,下单时先预扣,在规定时间内不付款再释放库存,即采用“预扣库存”这种方式呢?

这种方案确实可以在一定程度上缓解上面的问题。但是否就彻底解决了呢?其实没有!针对恶意下单这种情况,虽然把有效的付款时间设置为10分钟,但是恶意买家完全可以在10分钟后再次下单,或者采用一次下单很多件的方式把库存减完。针对这种情况,解决办法还是要结合安全和反作弊的措施来制止。

加锁set是为了防止多机用户同时访问同一共享资源,即库存数。

1.先扣减库存,防止用户抢购后未支付

2.定时任务扫描5分钟内未支付订单,并主动请求支付状态

3.关闭订单,重新释放库存

<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2020/7/7
* Time: 17:09
*/
//连接本地的 Redis 服务
/* Connect to an ODBC database using driver invocation */
$dsn = 'mysql:dbname=homestead;host=192.168.10.10';
$user = 'homestead';
$password = 'secret';


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

//存储数据到列表中
//for($i=0;$i<=10;$i++){
// $redis->lpush("goods_store",1);
//}
//var_dump($redis->lLen("goods_store"));
//exit;
$rand_num = rand(1,10);
//$rand_num =1;

//库存获取
$key = 'lock';
$random = rand(0,1000);
$ttl = time();
//NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
//EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
$rs = $redis->set($key, $random, array('nx', 'ex' => $ttl));
if ($rs) {
//处理更新缓存逻辑
$Len = $redis->lLen("goods_store");

//判断库存
if($Len >= $rand_num){

    //生成订单数据
    //....

     //下面这一段属于支付成功后执行,这样可以防止有些抢购了未支付情况
try {
$dbh = new PDO($dsn, $user, $password);
$dbh->beginTransaction();
$sql = "UPDATE `Course` SET `order_number`=order_number-$rand_num WHERE (`c_id`='03')";
$sth = $dbh->exec($sql);
$dbh->commit();

// 获取存储的数据并输出
for($i=1;$i<=$rand_num;$i++){
$arList = $redis->lpop("goods_store");
}
var_dump($redis->lLen("goods_store"),$arList);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}

//先判断随机数,是同一个则删除锁
if ($redis->get($key) == $random) {
$redis->del($key);
}
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM