问答

这句sql会导致并发出问题吗 ?

作者:admin 2021-04-19 我要评论

数据库里面 stock 字段是 int unsign , 这条sql update table_1 set stock = stock - 1 , 如果当 stock = 50 ,并发的时候,会不会导致超出50个请求 执行成功 ...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

数据库里面 stock 字段是 int unsign ,
这条sql
update table_1 set stock = stock - 1
如果当 stock = 50 ,并发的时候,会不会导致超出50个请求 执行成功 (正常情况下当stock=0的时候再执行就会报错)?
这条语句涉及mysql的锁的什么知识 ?

###

你这问题涉及的是数据库的4个隔离等级。而锁是并发控制的手段,两者是不同层次的概念。如果你把隔离等级设到最高(串行化)则应该没有并发的问题,但是吞吐会降低

###

REPEATABLE READ隔离级别下,如果stock上有索引,这条语句会锁住整个索引,如果没有索引,则会锁住全表,所以即使是50个并发,执行起来也是一个一个顺序执行的,当语句顺序执行到51次的时候应该就会报错了

###

结合你的业务场景来分析,本身这条 sql 是没问题的。
stock 为非负数,减到 0 时候回报错。
如果是一个商品的库存,然后做类似抢购业务场景,你是不是只允许减一次呢,那就要做幂等处理。
对应 MySQL 如果是 innodb 引擎,这个是加上排它锁的。

###

这个和mysql锁没什么关系,很明显 stock字段定义为无符号类型,即stock>=0才能更新成功

###

没有问题,因为update会先用当前读查出要修改的记录,然后再进行修改操作,而当前读是会对这些数据行加排它锁的,所以可以保证并发安全。下面是我写的一个小例子,有兴趣的可以自己实验一下,c1虽然早早就完成了更新,但是必然要等到c1睡眠时间结束后提交事务释放了排它锁,c2才能成功更新,在这之前c2是阻塞等待c1释放排它锁的

public static void main(String[] args) throws ClassNotFoundException, SQLException {

    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection c1 = null;//获取连接1
    Connection c2 = null;//获取连接2
    c1.setAutoCommit(false);
    c2.setAutoCommit(false);
    new Thread() {

        public void run() {
            System.out.println("c1开始更新");
            try {
                PreparedStatement ps = c1.prepareStatement("update test set num = num -1 where id = 1");
                ps.executeUpdate();
                ps.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("c1完成更新");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("c1睡眠结束");
            try {
                c1.commit();
                c1.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }.start();

    new Thread() {

        public void run() {
            System.out.println("进入线程2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                 e1.printStackTrace();
            }
            System.out.println("c2开始更新");
            try {
                PreparedStatement ps = c2.prepareStatement("update test set num = num -1 where id = 1");
                ps.executeUpdate();
                ps.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                 e.printStackTrace();
            }
            System.out.println("c2完成更新");
            try {
                c2.commit();
                c2.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }.start();
}

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • nginx响应速度很慢

    nginx响应速度很慢

  • 点击选中的多选框,会在已选那一栏显示

    点击选中的多选框,会在已选那一栏显示

  • PHP 多态的理解

    PHP 多态的理解

  • 关于C语言中static的问题

    关于C语言中static的问题

腾讯云代理商
海外云服务器