1.开发环境,nginx+mysql+Linux+php(thinkphp5)+redis。通过jmeter模拟并发场景。
模拟场景:500人同一时间抢购30部手机,通过PHP实现乐观锁。
public function sale_order(){
$user_id = mt_rand(1,100000);
$goods_info = Db::name('goods')->where('id',1)->find();
if($goods_info['goods_num'] <= 0){
return '商品已售罄';
}
$user_info = Db::name('wallet')->where('user_id',$user_id)->find();
if(empty($user_info)){
return '用户信息不存在';
}
if($user_info['money']<$goods_info['flash_sale_price']){
return '秒杀失败,余额不足';
}
$order_num = $this->get_order_num();
$order_info = [
'order_num' => $order_num,
'user_id' => $user_id,
'goods_id' => $goods_info['id'],
'num' => 1
];
$goods_data = [
'goods_num' => $goods_info['goods_num'] -1,
'version' => $goods_info['version'] + 1,
];
$goods_where_data = [
'id' => $goods_info['id'],
'version' => $goods_info['version']
];
$user_banlance = round(($user_info['money'] - $goods_info['flash_sale_price']),2);
Db::startTrans();
try{
$res = Db::name('goods')->where($goods_where_data)
->update($goods_data);
if($res != 1){
Exception('减库存失败',0);
}
Db::name('goods_order')->insert($order_info);
Db::name('wallet')->where('user_id',$user_info['id'])
->update(['money'=>$user_banlance]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
$log_data = [
'log' => $e->getMessage(),
];
Db::name('log')->insert($log_data);
return '下单失败';
}
return '下单成功';
}
2.运行结果如下:


从上图可以看到,在500并发的情况下,商品没有超卖,但是也没有卖光。因此需要在此基础上模拟用户重复刷新的动作。
3.这次用户下单失败后还会继续重复下单6次。


从上图可以看到,通过乐观锁,成功避免了超卖问题,类似的操作也可以通过PHP+Redis分布式锁实现。