mysql锁研究系列四(事务在并发情况下避免超卖)

来源:互联网 发布:js获取当前网页的url 编辑:程序博客网 时间:2024/06/05 19:26

我们在做电商的时候会考虑到一个问题,如果此时库存就剩一个,而来了两个并发同时下单,如何避免库存超卖?
先贴一段代码:

    beginTranse(开启事务)    $sql = "select * from goods where title='测试商品'";    $tmp = $db->query($sql);    $info= $db->fetch_assoc($tmp);    $time = time();    if($info['stock']>0)    {        $sql = "insert into test_order(name,price,create_time) values('{$info['title']}','{$info['price']}','{$time}')";        $db->query($sql);        $sql = "update goods set stock = stock -1 where title='测试商品'";        $db->query($sql);    }    commit(提交事务);

这段代码很简单 先查看库存,然后判断有库存就下单操作。
那么这样的情况会出现超卖吗?利用上节学到的知识分析:

  1. 两个事务同时读取同一个商品,inndb对select语句不会加锁,就算加共享锁,其他事务也可以读。所以两个事务得到的库存都是1
  2. 两个事务同时修改操作,mysql会将并发update语句串行化处理,一个一个执行。这样当第一个update操作的时候会生成排他锁,其他事务等待,当第一个事务提交后,第二个事务得到锁继续update操作。那这样的话库存就为-1了。

    上面的代码看似逻辑正常,其实会出现超卖,其实我们只要改一下sql语句就搞定了,在update下判断库存:

$sql = "update goods set stock = stock -1 where title='测试商品' and stock>0";

由于mysql的排他锁保持事务的原子性,update操作也是原子性。按照上面的逻辑走下来的时候:当第一个事务提交后,这行数据已经改变,而第二个事务update时候判断已经为0 了,就不会执行update操作!

关于高并发情况:例如秒杀、抢购之类的情况。mysql是没法支撑的。因为频繁的读写一行会造成锁等待和死锁,而这些所等待进程挂起是相当占mysql资源的。当然不排除像阿里这样的公司 将mysql源码修改,关闭死锁,在引擎层做队列,而且可以合并提交,例如对一行数据的100个update stock-1 语句 合并起来一条语句 update stock -100 。这些都涉及到底层修改。我们肯定没法考虑。其他关于秒杀的实现,这里不做探讨。网上的说法很多,原理差不多,但其实真正遇见时才会发现问题。

0 0
原创粉丝点击