先声明本人菜鸟,不喜勿喷。
模拟应用场景是,支付下单,减少库存。
我建了三个表,goods、orders、logs 都是用的InnoDB引擎,不然不支持事务
goods里面的字段,id,name,price,stock(库存)
orders里面有 id,uid,ordersn,good_id,createtime
logs 里面有 id,status,uid
以上表的id都是自增长的主键
用的tp3.2测试的
插入一条数据到goods
public function addgoods(){
$data = array(
'name' => '小米手机',
'price' => 100,
'stock' => 10
);
M('goods')->add($data);
}
然后就是弄一个创建订单的方法createOrder,同时更新了库存,在弄一个call调用
简单的写了下这个下单的过程,可以理解为,就一个insert一个update 不带事务
public function call(){
$this->createOrder(1, 1);
}
public function createOrder($uid,$goodid){
$good = M('goods')->find(1);
if(!$good['stock'] > 0) {
return false;
}
$data = array(
'ordersn' => NOW_TIME.rand(1000000,9999999),
'good_id' => $goodid,
'createtime' => NOW_TIME,
'uid' => $uid
);
$orderid = M('orders')->add($data);
$stock = $good['stock'] - 1;
$goodupdate = M('goods')->where('stock > 0 and id = 1' )->save(array('stock' => $stock));
if($orderid && $goodupdate){
$status = '成功';
}else{
$status = '失败';
}
return M('logs')->add(array('uid' =>$uid,'status' => $status));
}
然后下载了一个叫ab的所谓web性能测试工具
我们就调用call方法 100次试试。
发现库存确实是0了(减少了10个),但是orders表里面有47条数据。我滴个龟龟。。好吓人
如果说这是一个秒杀,100个人同时去买,有47个订单是成功的。但是库存却只有10个。
如果并发更高呢?后果不堪设想。。
我们加添加事务试试。。
public function createOrder($uid,$goodid){
M()->startTrans();
$good = M('goods')->find(1);
if(!$good['stock'] > 0) {
return false;
}
$data = array(
'ordersn' => NOW_TIME.rand(1000000,9999999),
'good_id' => $goodid,
'createtime' => NOW_TIME,
'uid' => $uid
);
$orderid = M('orders')->add($data);
$stock = $good['stock'] -1;
$goodupdate = M('goods')->where('stock > 0 and id = 1' )->save(array('stock' => $stock));
if($orderid && $goodupdate){
$status = '成功';
M()->commit();
}else{
$status = '失败';
M()->rollback();
}
return M('logs')->add(array('uid' =>$uid,'status' => $status));
}
加了这几句, 其他代码也没变
M()->startTrans();
M()->commit();
M()->rollback();
库存还原到10个,清空logs和orders,再跑一边看结果吧。
库存依然正常到0了,但是orders有28条数据,啥情况。。。是不是事务写错了,还是tp里面的事务有bug?这不是闹眼子吗。。
然后我特意去测试tp的事务,写一个update然后回滚,看行不行。。结果是可以的。。
那么我这个代码到底哪里出了问题。。写了事务咋还不行?
原来忘记了一个大问题,那就是没锁表。。。
我们把
$good = M('goods')->find(1);
修改为
$good = M('goods')->lock(true)->find(1); // lock相当于是加了个for update 我们用的InnoDB支持行级锁
再试试。。
库存正常为0,orders和logs都只有10条数据了。应该就正常了。。
我们在试试1000个并发,依然是稳稳的10条数据。。
如果只是小量的并发,用事务和锁表完全可以解决。