PHP+Redis交易高并发解决商品超卖问题
在一些有一定用户量的电商网站,如果只使用关系型数据库(如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。
然后我们使用ab工具模拟1000个请求和50个并发来测试
ab的1000。test.com/
然后我们使用Redis桌面管理器查看一些Redis结果
在优惠券订单队列中有10个用户的信息
并且剩余的优惠券数量也为0,不再是负数
同时还存储了10个用户用户优惠券集合的UID数据中。
上面的代码解决了两个问题:
解决了大量立即查询数据库,给数据库造成很大压力的问题。在redis缓存层拦截流量
解决因库存过多导致优惠券中断的问题
不过这段代码也存在一定的问题:
没有使用Redis连接池,重复创建新的redis对性能有一定的影响
由于使用事件,导致只有一个用户抢到优惠券每个。并发请求如果成功,该并发请求的其他用户将会失败,只能等待下一次并发
也是事件导致的存储问题。如果有10个产品,每次1000个请求,200个同时请求,5个同时请求,完成1000个请求,但只有5个用户设法捕获它。如果没有后续请求,库存还剩5张
提示:在消费队列中,如果优惠券发放失败,应立即保存并短信通知执行主任,以便可以看到是否可以重新发送或者通过后台手动发送给用户。
所以,后来我用lua脚本和redis来解决这个问题。我会弄清楚并稍后填写代码。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。