//在tp6中think\cache\Driver代码中添加返回缓存对象方法:
/**
* @desc 获取缓存操作对象
* @return object
*/
public function getHandler(){
return $this->handler;
}
//在项目config/cache.php文件中添加redis配置信息
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => '6379',
'password' => '123456789',
'select' => '0',
// 全局缓存有效期(0为永久有效)
'expire' => 0,
// 缓存前缀
'prefix' => '',
'timeout' => 0,
],
//添加随机goods信息,及将goods库存添加如redis中
public function addGoods(){
$redis = Cache::store('redis')->getHandler();
$data = [];
for($i = 1;$i < 101;$i++){
$store = intval(rand(1000,9999));
$data[] = [
'id' => $i,
'goods_name' => $this->getChar(5),
'goods_store' => $store,
'goods_price' => round(rand(1000,9999)/100,2),
];
$key = "goods_stock_".$i;
$redis->set($key, $store);
}
$ids = Db::name('test_goods')->insertAll($data);
echo $ids;
}
//php随机生成汉字 $num为生成汉字的数量
public function getChar($num){
$b = '';
for ($i=0; $i<$num; $i++) {
$a = chr(mt_rand(0xB0,0xD0)).chr(mt_rand(0xA1, 0xF0));
$b .= iconv('GB2312', 'UTF-8', $a);
}
return $b;
}
//循环随机买家购买订单
public function doOrder(){
$user_list = Db::name('test_user')->orderRaw('rand()')->select();
foreach($user_list as $k=>$v){
$result = $this->buyOrder($v['id'], 97);
if($result['status'] == 0){
echo $result['message'].'<br/>';
}else{
echo '成功下单:'.$result['message'].'<br/>';
}
}
}
/**
* 下单 redis乐观锁,是用一个watch,就是去加一个监视器。
* @return \think\Response
*/
public function buyOrder($user_id, $goods_id)
{
$redis = Cache::store('redis')->getHandler();
//goods库存key
$goods_key = 'goods_stock_'.$goods_id;
//监听对应的key
$redis->watch($goods_key);
//获取goods库存
$goods_store = $redis->get($goods_key);
//抢购成功买家集合key
$orderUserKey = 'goodsOrderUserSet';
//判断买家是否在集合中,在则说明已购买。
$status = $redis->sismember($orderUserKey, $user_id);
if($status){
return ['status'=>0, 'message'=>$user_id.'已抢过订单'];
}
//抢购成功买家队列key
$buyUserList = 'goodsBuyUserList';
if($goods_store > 0){
$redis->multi();//开启事务
$redis->decr($goods_key);//减少库存
$redis->sadd($orderUserKey, $user_id);//将买家id添加到抢购成功买家集合中
$res = $redis->exec(); //执行所有事务块内的命令
if($res){
//将goods和买家id放入队列中,再之后继续循环生成订单操作。这边也可以将信息存入消息队列。
$redis->lPush($buyUserList, $goods_id.'_'.$user_id);
return ['status'=>1, 'message'=> $user_id.'下单成功'];
}else{
return ['status'=>0, 'message'=> $user_id.'商品库存执行失败'];
}
}else{
return ['status'=>0, 'message'=> $user_id.'商品库存不足,请下次再购买'];
}
}
//秒杀循环生成订单
public function afterOrder(){
$begin_time = time();
echo '开始时间:'.date('Y-m-d H:i:s',$begin_time).'<br/>';
$buyUserList = 'goodsBuyUserList';
$redis = Cache::store('redis')->getHandler();
$len = $redis->llen($buyUserList);
while($len > 0){
$order_info = $redis->rPop($buyUserList);
if($order_info){
$orders = explode('_', $order_info);
$goods_id = $orders[0];
$user_id = $orders[1];
if(!empty($goods_id) && !empty($user_id) && intval($goods_id) && intval($user_id)){
// 启动事务
Db::startTrans();
try {
$user_obj = Db::name('test_user')->where('id', $user_id)->find();
$goods_obj = Db::name('test_goods')->where('id', $goods_id)->find();
//更新商品库存信息
$goods_result = Db::name('test_goods')->where('id', $goods_id)->dec('goods_store')->update();
//添加订单信息
$order_data = [];
$order_data = [
'order_no' => date('YmdHis').rand(100000,999999),
'buyer_id' => $user_id,
'buyer_name' => $user_obj['true_name'],
'goods_id' => $goods_id,
'goods_num' => 1,
'goods_price' => $goods_obj['goods_price'],
'add_time' => date('Y-m-d H:i:s'),
];
$order_id = Db::name('test_order')->insertGetId($order_data);
// 提交事务
Db::commit();
error_log($user_id.'=='.$goods_id.'生成订单成功,订单id:'.$order_id.',订单号为:'.$order_data['order_no']."\r\n", 3, 'order_'.date('Ymd').'.log');
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
error_log($user_id.'=='.$goods_id.'生成订单失败'."\r\n", 3, 'order_'.date('Ymd').'.log');
}
}
}
$len = $redis->llen($buyUserList);
}
$end_time = time();
echo '结束时间:'.date('Y-m-d H:i:s',$end_time).'<br/>';
echo '总操作时间:'.($end_time-$begin_time);
}
08-12
586
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
04-08
4245
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
08-19
5514
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)