Code前端首页关于Code前端联系我们

PHP+Redis交易高并发解决商品超卖问题

terry 2年前 (2023-09-24) 阅读数 61 #后端开发

在一些有一定用户量的电商网站,如果只使用关系型数据库(如MySQL、Oracle)进行繁忙的采购,数据库会非常高,而且如果数据库锁定机制使用不当,也会导致产品和优惠券超卖的问题。

我的公司也面临着同样的问题。购买过多优惠券时出现问题。当问题出现后,我们就开始思考解决问题的方法。由于我经常使用redis,所以我打算使用redis来解决这个问题。问题。

利用redis的高性能和交易能力,解决线上优惠券积压问题。下面我展示了我用来临时解决这个问题的伪代码的第一个版本,但删除了一些细节:

/ **

* Grab Coupons (Flash Sale)

* @param int $couponId Product ID

* @param int $uid 用户名

* @return bool

*/

function secKill($couponId, $uid)

{

//1.初始化Redis连接

$redis = new Redis();

if (!$redis->connect('127.0.0.1' , 6379)) {

trigger_error('Redis连接错误!!!' , E_USER_ERROR);

} else {

echo '连接正常
';

}

//秒杀产品库存key

$key = 'secKill:'.$couponI. ' :stock';

$redis->watch($key);

//获取库存

$stock = $redis->get($key);

//闪购优惠未启动表示库存为空

if (!$stock && !is_numeric($stock)) {

echo '闪购尚未开始';

return false;

} ❝ //谴责库存。如果库存大于0,则减少库存,并将抢购成功的用户添加到哈希表中。如果小于等于0,闪购结束

if ($stock sIsMember('secKill:'.$couponId.':uid', $uid)) {

echo '第二次杀掉失败';

return false;

}

/ / 代码到达此点,表明该用户是第一次参加限时抢购。 storage减一,然后将person放入collected集合表中

//multi(),返回一个redis对象并进入多态模式,进入多态模式后调用所有方法都返回相同object,

//直到调用exec()方法。

$result = $redis->multi()->decr($key)->sAdd('secKill:'.$couponId.':uid', $uid)->exec();

if (empty($result)) {//交易已取消

echo '闪购失败';

return false;

}

//获取优惠券并设置优惠券ID。进入队列中,一个单独的进程队列消费排队的数据,并将抓取到的优惠券推送给用户

$redis->lPush('couponOrder', $couponId.'+'.$uid);

$redis ->close();

返回 true;

}

$couponId = 11211;

$uid = mt_rand(1, 100);

secKill($uid) ;

First我模拟设置优惠券id为11211的优惠券库存为10。

PHP+Redis事务解决高并发下商品超卖问题

然后我们使用ab工具模拟1000个请求和50个并发来测试

ab的1000。test.com/

然后我们使用Redis桌面管理器查看一些Redis结果

在优惠券订单队列中有10个用户的信息

PHP+Redis事务解决高并发下商品超卖问题

并且剩余的优惠券数量也为0,不再是负数

PHP+Redis事务解决高并发下商品超卖问题

同时还存储了10个用户用户优惠券集合的UID数据中。

PHP+Redis事务解决高并发下商品超卖问题

上面的代码解决了两个问题:

解决了大量立即查询数据库,给数据库造成很大压力的问题。在redis缓存层拦截流量

解决因库存过多导致优惠券中断的问题

不过这段代码也存在一定的问题:

没有使用Redis连接池,重复创建新的redis对性能有一定的影响

由于使用事件,导致只有一个用户抢到优惠券每个。并发请求如果成功,该并发请求的其他用户将会失败,只能等待下一次并发

也是事件导致的存储问题。如果有10个产品,每次1000个请求,200个同时请求,5个同时请求,完成1000个请求,但只有5个用户设法捕获它。如果没有后续请求,库存还剩5张

提示:在消费队列中,如果优惠券发放失败,应立即保存并短信通知执行主任,以便可以看到是否可以重新发送或者通过后台手动发送给用户。

所以,后来我用lua脚本和redis来解决这个问题。我会弄清楚并稍后填写代码。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门