防止负库存方案小总结

  一、直接操作数据库,SQL如下:

update t_warehouse_material set count = (count- 10)

 where warehouse_number ='center_sh' and material_number='Y21002' and batch_number='p_20170822_2' and count>=10

update t_warehouse_material set count = (count- 10)

 where warehouse_number ='center_sh' and material_number='Y21002' and batch_number='p_20170822_2' and count-10>=0


优缺点:不具备通用性,不能记录操作前后日志;

此种方式在高并发环境下会产生数据库性能影响。

 

第二种方式:直接给数据库字段添加约束,例如像 库存、成绩、年龄 等等这些常规字段正常是不能为负数的,

alter table t_warehouse add constraint CN_Count check (count>=0);

 

二、select XX for update

select for udpate 则是悲观锁在查询数据的时候,就将数据锁住

 Product product = select * from table where name = productName for update;  

    if (查询的剩余数量 > buyQuantity) {  

        影响行数 = update table set surplus = (surplus - buyQuantity) where name = productName ;  

    } else {  

        return "库存不足";  

    }

 

优缺点: 推荐使用,最简单的方案,但是如果事务过大,会有性能问题.操作不当,会有死锁问题。

 

三、CAS (compare/check and swap/set)

乐观锁: 使用version数据库版本号字段来控制

update 时将version值加1,看select时的version和下一步update时的version是不是同一个版本号,以此来判断当前的数据是不是之前查询到的数量值,还是已被其它线程已经修改过后的数量值。

 

update t set surplus = 90 ,version = version+1 where id = x andversion = oldVersion ;  

 

优缺点:推荐使用,但是如果数据竞争激烈,则自动重试次数会急剧上升,需要注意。

 

四、Java 里synchronized同步关键字

public synchronized void buy(String productName, Integer buyQuantity) {  

    // 其他校验...  

    // 校验剩余数量  

    Product product  = 从数据库查询出记录;  

    if (product.getSurplus < buyQuantity) {  

        return "库存不足";  

    }  

      

    // set新的剩余数量  

    product.setSurplus(product.getSurplus() - quantity);  

    // 更新数据库  

    update(product);  

    // 记录日志...  

    // 其他业务...  

}  

 

优缺点:

1).synchronized 作用范围是单个jvm实例,如果做了集群,分布式等,就没用了

2).synchronized是作用在对象实例上的,如果不是单例,则多个实例间不会同步(这个一般用spring管理bean,默认就是单例)

3).单个jvm时,synchronized也不能保证多个数据库事务的隔离性.这与代码中的事务传播级别,数据库的事务隔离级别,加锁时机等相关.

五、给数据库字段添加范围约束

-- 约束 count 字段必须大于等于0,即库存不能为负数

alter table t_warehouse add constraint CN_Count check (count>=0);

 

你可能感兴趣的:(Java,线程/多线程,调优/优化)