
【场景】偶有客户提单反馈,单据拆单生成多个下游,但是保存/审核反写后的写到上游单据上的数据和历史单据不匹配问题
【分析】
<1>平台反写的实践案例:采购入库单反写采购订单的累计入库数量,增加100
```sql
--方案一:利用数据库累加更新
update t_pur_poorderentry_r set fbasestockqty = fbasestockqty + 100 where fentryid = @fentryid
--方案二:查询后,覆盖更新
select fbasestockqty from t_pur_poorderentry_r where fentryid = @fentryid;--假设查询历史值为200
update t_pur_poorderentry_r set fbasestockqty = 300 where fentryid = @fentryid;
```
上为两个数据库的更新实践,如果是按照方案一是不会有什么问题的,数据库保证行锁原子性累加;
但是目前反写不是使用的方案一,而是使用的方案二,基于以下考量:
a)反写需要超额控制验证,因此读取了源单数据
b)反写的字段较多,需要逐字段更新会有更多的更新脚本,性能考量较差,而是使用ORM整表映射更新的逻辑进行记录更新
c)还需要根据更新后的值做关闭控制,因此必须知道更新后是多少,才能确保是否更新状态字段为关闭状态
方案二正确的前提是,查询的值必须为数据库的真实值,方案二在并发情况下,必然会导致丢失更新,参考下图逻辑:

用户B入库单保存数量200,在并发时把用户A入库单保存数量的100覆盖了,导致最终入库单的累计入库数量只有200,而最终需要的应该是300;
<2>历史版本平台反写的网控互斥
网控互斥的初衷就是用来解决用户并发操作的,但是历史版本的反写网控设计确实存在缺陷
历史版本的设计,反写网控是在反写计算完成后就释放网控的,但是数据没有提交

如上图所示,用户A创建网控计算反写时,由于网控的存在,用户B无法申请读取数据,因此事务操作会直接中断(解释网控是如何解决并发操作的,通过冲突操作直接中断用户B的保存);
但是,同样的由于用户A的反写网控过早的释放,事务还没有提交,数据库中依然为0,因此存在可能用户C调用保存时,网控已经释放但是数据没有提交,最终用户C保存时会把用户A的覆盖掉(网控过早释放导致的更新覆盖问题)
<3>升级后平台反写的网控互斥(需要手工操作升级)
补丁