晓夏

北漂的女孩

Good Luck To You!

PHP+redis实现秒杀,解决并发问题

浏览量:494

  1. 秒杀商品添加信息,生成配置文件,并保存好数据

/**
 * 新增
 * @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的原子自增和自减,就可以实现这个问题。里面省略了很多细节。希望你也能完成,加油

图片.png

神回复

发表评论:

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