- 抢购
主要针对两个问题
一、高并发对数据库产生的压力
二、竞争状态下如何解决库存的正确减少("超卖"问题)
解决
第一个问题,对于PHP来说很简单,用缓存技术就可以缓解数据库压力,比如memcache,redis等缓存技术。
代码参考
// 关闭自动提交
$this->db_conn->autocommit(FALSE);//开启事务
//获取商品库存
$query_sql = 'select stock from goods where id ='.$goods_id .' lock in share mode'; //加mysql共享锁[提交前不允许其他事务修改]
$stock = $this->query($query_sql);
if ($stock < $num) {
return $this->log('库存不足_'.$stock);
}
//减库存
//创订单
//提交事务
REDIS
以商品id生成key,redis获取库存,开启redis监控key和redis事务首次获取失败: 数据查询商品库存存入redis'
/**
* 生成商品库存redis
*/
public function redis()
{
$data = [
['id' => 1,'stock' => 2],
['id' => 2,'stock' => 23],
['id' => 3,'stock' => 1]
];
foreach ($data as $v) {
$redis_key = 'goods_id_' . $v['id'];
for ($i = 0; $i < $v['stock']; $i++) {
//插入redis中商品的表抢购时从这读取
$this->redis->lPush($redis_key, 1);
}
}
$this->success('生产库存成功');
}
/**
* 抢购
*/
public function skill()
{
$data = input();
//模拟随机用户id
$uid = rand(10000,99999);
//传商品id
$redis_key = 'goods_id_' . $data['id'];
//查询redis中商品的数量长度
$len = $this->redis->lLen($redis_key);
$str = "";
if(!$len || $len<=0){
$str .= $uid."抢购失败,商品售罄";
}else{
$redis_user_name = "goods_id_".$data['id']."_uid";
//判断已抢购列表中是否已存在该用户,获取列表中所有的数据
$skill_user_list = $this->redis->lrange($redis_user_name, 0, -1);
if(in_array($uid, $skill_user_list)){
$str .= $uid."已经抢购成功";
}else{
//从redis商品表中取出一个
$this->redis->rPop($redis_key);
//插入用户抢购成功的redis表中
$this->redis->lPush($redis_user_name,$uid."_".microtime().rand(10000,99999));
$str .= $uid."抢购成功";
}
}
//存日志信息与流程无关
$path = __DIR__ . "/../../../redis_log.txt";
$file = fopen($path, "a+"); //创建文件或打开文件
fwrite($file, '抢购信息' . '----' . $str . '-------------------' . '时间:' . date('Y-m-d H:i:s', time()) . "\r\n");
echo '完成';
}