在做ERP订单接入仓库库存的时候出现了一个问题。下单会读取该商品是否有库存。如果有那么就下单 减去(可下单库存,物理库存不变) 没有则提示库存不足
在并发情况下就会导致 u1用户下单编号为1的商品1个 u2用户也下单编号为1的商品1个。编号为1的商品库存只有1个。 那么当u1在读取出数据的时候发现库存有一个 但是还没有写入库存的操作 u2也下单成功并提交 那么就会导致数据错乱 本来应该是只有u1会下单成功u2则提示库存不足
这里可以用事物的隔离机制或乐观锁来解决
事物是一个逻辑单元
原子性:事物里面是一块逻辑单元 要么都执行 要么都不执行
一致性:事物处理前与处理后的状态的要是一致的(a账户有200元 b账户有300元 共计500元 a账户给b账户转账100元。事物处理后2个账户总额也为500元)
隔离性:每个事物都有自己的数据空间,使事物的处理结果不会被别的事物所影响
持久性:事物提交 数据就永久的保存下来了
事物的隔离级别
Read uncommitted 读取未提交的
这个隔离级别可能会导致脏读。它可以读取别的事物更改后并未提交的数据。比如以仓库库存判断为例子 在一个事物里面读取商品 并修改商品库存为0但是事物还没有提交 另外一个事物下单 发现没有库存 提示库存不足 这个时候第一个事物取消 导致脏读
Read committed 读取已经提交的
这个隔离级别能够避免脏读。因为只能读取事物已经提交的数据。但是会出现不可重复读 还是以仓库为例子 一个事物下单一个商品 数量为1 库存也为1 当下单的事物还没有提交 另外一个事物已经下单并修改了库存为0 造成数据混乱 因为这个事物隔离级别虽然只能读取已经提交的 但是能够修改未提交的数据
Repeatable read 可重复读
这个事物隔离级别可以解决上面的那种情况 当一个事物在处理某条数据的时候 别的事物既不能读取也不能修改 但是会造成幻读 因为不能修改和读取未提交的数据 但是可以插入和删除数据 比如个人账户消费为例子 一个事物查询用a当月消费记录总和来做信用评分 select sum(money) from table 读取出来当月消费2000元 。当事物还没有提交 用户a老婆这个时候在美容院消费5000 那么table表新增一条消费记录为5000的数据 这个时候出现幻影读(事物读取数据与实际数据不符)
Serializable 序列化
这个隔离级别最高 能够避免 脏读 不可重复读 幻影读 但是效率低(既一个事物在操作某条数据的时候 别的事物不能读取和修改这条数据 也不能往表里插入数据)
根据不同的并发场景选择不同的事物隔离机制
乐观锁
即使用数据在解决并发情况下数据不一致的情况
以仓库库存为例
在仓库表增加一个最后修改时间字段 并将它作为条件
仓库库存表拥有a产品 可下单库存1个
u1用户下单a产品 读取出来判断库存是否充足 充足的话 在事物里面下单并更新可用库存 update stok set num-=1,lastupdatedatetime=datetime.now where id=a and lastupdatedatetime=上一次的修改时间
就算在u1提交之前别的事物比他先提交 那么u1 提交的时候 lastupdate被别的事物更新了 则条件不成立提交失败(受影响行数为0 回滚下单操作)
注:Oracle 只支持Serializable Read committed