释放双眼,带上耳机,听听看~!
那么,既然“下单减库存”和“付款减库存”都有缺点,我们能否把两者相结合,将两次操作进行前后关联起来,下单时先预扣,在规定时间内不付款再释放库存,即采用“预扣库存”这种方式呢?
这种方案确实可以在一定程度上缓解上面的问题。但是否就彻底解决了呢?其实没有!针对恶意下单这种情况,虽然把有效的付款时间设置为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); } }