PHP+redis实现秒杀,解决并发问题
浏览量:522
秒杀商品添加信息,生成配置文件,并保存好数据
/**
* 新增
* @param $goodsid
* @return bool|string
* author : lianghuiju@chuchujie.com
* function_name : insertRedisSeckillActivity
* description :
*/
public function insertRedisSeckillActivity($goodsid,$data) {
$this->load->driver('cache');
$resinfo = $data;
if ($resinfo) {
$key = "seckill_goodsinfo_{$goodsid}";//详情信息
$keyid = "seckill:goodsall:id";//秒杀商品
$key_group = "seckill_group_{$goodsid}";//成团数
$time_diff = ($resinfo['end_time'] - time()) + 1800;
$detailgoods = $this->cache->redis->save($key, json_encode($resinfo), $time_diff);//秒杀商品的详细信息
for ($i = 1; $i <= $resinfo['group_num']; $i++) {//成团数
$this->cache->redis->LPUSH($key_group, 1);
}
$this->cache->redis->EXPIRE($key_group, $time_diff);//设置过期时间
$resllen = $this->cache->redis->llen($key_group);
$keyidres = $this->cache->redis->zadd($keyid, 1, $resinfo['goods_id']);
if ($resllen == $resinfo['group_num'] && $keyidres >= 0 && $detailgoods) {
return true;
} else {
return false;
}
}
}
/**
* 更新redis
* author : lianghuiju@chuchujie.com
* function_name : updateRedisSeckillActivity
* description :
*/
public function updateRedisSeckillActivity($goodsid,$data) {
$this->load->driver('cache');
$resinfo = $data;
$key = "seckill_goodsinfo_{$goodsid}";//详情信息
$keyid = "seckill:goodsall:id";//秒杀商品
$key_group = "seckill_group_{$goodsid}";//成团数
if ($resinfo) {
$time_diff = ($resinfo['end_time'] - time()) + 1800;
$detailgoods = $this->cache->redis->save($key, json_encode($resinfo), $time_diff);//秒杀商品的详细信息
$this->cache->redis->del($key_group);
for ($i = 1; $i <= $resinfo['group_num']; $i++) {//成团数
$this->cache->redis->LPUSH($key_group, 1);
}
$this->cache->redis->EXPIRE($key_group, $time_diff);//设置过期时间
$resllen = $this->cache->redis->llen($key_group);
$keyidres = $this->cache->redis->zadd($keyid, 1, $resinfo['goods_id']);
if ($resllen == $resinfo['group_num'] && $keyidres >= 0 && $detailgoods) {
return true;
} else {
return false;
}
}
}
/**
* @param $goodsid
* @return mixed
* author : lianghuiju@chuchujie.com
* function_name : deleteRedisSeckillActivity
* description :
*/
public function deleteRedisSeckillActivity($id) {
$this->load->driver('cache');
$where = array('id' => $id);
$resinfo = $this->getOneSeckillActivity($where);
$goodsid = $resinfo['goods_id'];
$key = "seckill_goodsinfo_$goodsid";//详情信息
$key_group = "seckill_group_$goodsid";//成团数
$keyid = "seckill:goodsall:id";//秒杀商品
$resid = $this->cache->redis->zrem($keyid, $goodsid);
$res = $this->cache->redis->del($key);
$resgroup = $this->cache->redis->del($key_group);
if ($res >= 0 && $resgroup >= 0 && $resid >= 0) {
return true;
} else {
return false;
}
}2.下单区分是秒杀还是普通商品
//TODO 判断普通商品秒杀商品
$this->load->driver('cache');
$key = "seckill:goodsall:id";
$seckill_goodsall = $this->cache->redis->Zrange($key, 0, -1);
$goodsid = $this->order_data[0]->items[0]->product_id;
if (in_array($goodsid, $seckill_goodsall)) {//秒杀商品
$this->skillMakeOrder();
return;
}3.下单解决并发问题,因为是拼团,所以有团长和团员区分
if (empty($shop['item']->team_sign) && $shop['item']->buy_type == OT_TEAM) {
$team_sign = substr(md5($order_sn), 8, 16);
$team_first = 1;
//如果是团长 不做任何判断 生成团员队列
$key = "seckill_goodsinfo_{$shop['item']->product_id}";
$res = json_decode($this->cache->redis->get($key), true);
$key_peoplenum = "seckill_peoplenum_{$shop['item']->product_id}_{$team_sign}";
$time = $res['end_time'] - $res['start_time'];
for ($i = 1; $i < $res['group_peoplenum']; $i++) {//团人数
$this->cache->redis->LPUSH($key_peoplenum, 1);
}
$this->cache->redis->EXPIRE($key_peoplenum, $time);//设置过期时间
} else {
$team_sign = $shop['item']->team_sign;
$key_group = "seckill_group_{$shop['item']->product_id}";//团数
$key_peoplenum = "seckill_peoplenum_{$shop['item']->product_id}_{$team_sign}";//团人数
$team_first = 2;
//如果是团员 团人数减去1
$res = $this->cache->redis->multi()->RPOP($key_peoplenum)->LLEN($key_peoplenum)->exec();
log_message("LOG", "CommoditySeckillPeoplenum Exception: {$key_peoplenum}_{$res[0]}_{$res[1]}");
//TODO $res[0] 代表RPOP
if ($res[0] != 1) {
throw new Exception('此团人数已满', self::ERROR_TEAM_NUM);
}
//TODO $res[1] 代表LLEN
if ($res[1] == 0) {//组团成功
//记录组团成功
$keyid = "teamsign_success_group_{$shop['item']->product_id}";
$this->cache->redis->zadd($keyid, 1,$team_sign);
$group = $this->cache->redis->multi()->RPOP($key_group)->LLEN($key_group)->exec();
log_message("LOG", "CommoditySeckillgroup Exception: {$key_group}_{$res[0]}_{$res[1]}");
if ($group[0] == false) {
$group = $this->cache->redis->multi()->RPOP($key_group)->LLEN($key_group)->exec();
}
//TODO $group[0] 代表RPOP减失败或者获取失败
if ($group[0] != 1) {
throw new Exception('请重试……', self::ERROR_REDIS);
}
//TODO $group[1] 代表LLEN 组团成功 商品下架
if ($group[1] == 0) {
$is_xiajia = true;
}
}
}最后:简单的几行代码就能控制住并发量,是不是很简单,只要你考虑到redis的原子自增和自减,就可以实现这个问题。里面省略了很多细节。希望你也能完成,加油


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