前提为每人限购1件
开抢前
把秒杀商品库存存进 Redis 队列中
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
//库存
$num = 10;
//往队列 goods_store 插入商品, 队列的长度为库存
for($i=0;$i<$num;$i++)
$redis->lpush('goods_store', 1);
开抢中
方法1
前端: 用户点击购买按钮进行form表单提交
后端: 执行下面代码
//用户等待队列
$wait_key = "user_wait";
//库存队列
$store_key = "goods_store";
//根据Redis hash特性, 设置成功返回1, 旧值被覆盖则返回0, 用来控制同一用户多买现象
$result = $redis->hset($wait_key, $user_id, $user_id);
if($result){
$count = $redis->lpop($store_key);
if(!$count)
return '已经抢光了';
//下单流程, 数据库入库等操作
//下单失败或报错则执行 $redis->hdel($wait_key, $user_id); 和加库存 $redis->lpush('goods_store', 1); 并跳转回上一页提示下单失败
......
//下单成功则跳转到相应页面
return '抢购成功';
}
问题: 高并发下可能造成服务器压力瞬间过大, 导致数据入库失败, 可将下单入库等流程用crontab定时器异步执行
方法2
前端: 用户点击购买按钮, 按钮变灰防止用户重复点击, 并且弹出小窗口提示排队中
ajax异步调用抢购接口
- 成功: js轮询请求是否下单成功接口
- 成功: 跳转相应页面进行支付流程
- 失败: 提示用户重新进行购买流程
- 失败: 提示用户重新进行购买流程
抢购接口代码
//用户等待队列
$wait_key = "user_wait";
//抢购成功的用户队列
$user_key = "user";
//库存队列
$store_key = "goods_store";
//根据Redis hash特性, 设置成功返回1, 旧值被覆盖则返回0, 用来控制同一用户多买现象
$result = $redis->hset($wait_key, $user_id, $user_id);
if($result){
$count = $redis->lpop($store_key);
if(!$count)
return '已经抢光了';
$redis->lpush($user_key, $user_id);
return '抢购成功';
}
crontab定时器异步执行下单入库代码
//用户等待队列
$wait_key = "user_wait";
//抢购成功的用户队列
$user_key = "user";
$count = $redis->rpop($user_key);
if(!$count)
return;
//下单流程, 数据库入库等操作
//下单失败或报错则执行 $redis->hdel($wait_key, $user_id); 和加库存 $redis->lpush('goods_store', 1);
......
是否下单成功接口代码
//数据库查询order订单表返回是否存在未支付订单数据
待解决:
假设用户A流程进行到以下逻辑时
流程1.下单流程, 数据库入库等操作
流程2.下单失败或报错则执行$redis->hdel($wait_key, $user_id);
和加库存$redis->lpush('goods_store', 1);
假设1由于未知错误导致程序崩溃, 没有执行2就退出了, 用户A不能重新抢购
假设1由于未知错误导致入库失败, 2在执行$redis->hdel
失败了, 用户A不能重新抢购
优化:
前端静态资源上CDN
设置nginx的最大连接数
假设秒杀商品库存有10个, 当用户等待队列 user_wait 长度大于 30~100 后的请求全部过滤
添加一个延时队列, 把下单规定时间内没有付款的订单取消掉, 并加库存
附送 Redis 锁简易代码
// 加锁 $random:随机数 $expire_time:有效时间
$lock_status = $redis->set($lock_key, $random, array('nx', 'ex' => $expire_time));
if($lock_status){
// do something
......
if($redis->get($lock_key) == $random){
// 解锁
$redis->del($lock_key);
}
}