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

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

    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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值