Mysql之事务(并发)初体验

来源:互联网 发布:手机淘宝退货流程图 编辑:程序博客网 时间:2024/06/05 14:01

先声明本人菜鸟,不喜勿喷。

模拟应用场景是,支付下单,减少库存。

我建了三个表,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条数据。。

如果只是小量的并发,用事务和锁表完全可以解决。

原创粉丝点击