功能的需求:
增加对商品订单的修改功能;也就是可以对订单中的商品进行更换及数量的修改;
我的预期编写方案是:
由于商品在下单中,进行了很多其他的数据库修改行为,例如扣除库存,统计,会员积分等等;所以我预想的是如下操作:
1.对订单进行整体回滚操作,例如删除统计,回滚库存,回滚会员积分余额等;
2.使用之前下单分离出的各个处理方法,重新对该订单下单,进行一系列操作;
于是,我意识到了一个问题,就是如此大的数据库修改操作,目前由于体量问题,又是在一个事务中完成,也就是有一个数据库长事务的操作,务必会引发死锁;
死锁发生的地方,只举出一个比较明显会出问题的地方,就是在订单修改和下单同时并发进行的时候;举例说明:
现有商品四种分别是:
| A商品 |
| B商品 |
| C商品 |
| D商品 |
下面假设发生并发行为:
| 管理员修改订单 | 客户端下单 |
| 对C.D商品进行库存回滚修改 | |
| 下单A.C商品 (此时由于修改订单原因,触发C商品的排他锁,客户端事务等待状态) | |
| 由于修改后,对A.B商品重新下单(此时由于客户端A商品占有锁,并且无法释放,导致客户端发生死锁,订单修改成功) |
同时如果加入了会员信息回滚,如果在同一事务中,一样也会发生死锁:
| 管理员修改订单 | 客户端下单 |
| 回滚A会员余额; | 扣减A商品库存, |
| 并且扣减A会员余额; (此时处于等待状态) | |
| 回滚A商品库存;(此时发生死锁) |
原因:
其实也看出来了,就是一个资源循环占用锁,互相无法释放的原因导致的;
从上面可以看出,用最基本的方式对商品进行排序,使发生数据修改的资源有序化已经无法避免死锁;如果只是下单扣库存则可以避免死锁;但是像修改订单这个长事务,已经改变了有序性;
解决方案:
1.如果并发量不高,并且可以容忍死锁造成的一次单方失败,则可以保持不动;(mysql会认为回滚事务代价最小的一方回滚)
2.优化代码,减少事务的处理时间,降低并发;
3.如果可以将两次修改行为分离出来,进行有序化;例如将回滚商品和扣除商品分离出来,进行排序合并后一并更新;
4.对同一事物中,所有资源进行有序化修改;
5.对功能进行重构,例如使用消息等异步最终一致性方式,减少长事务的存在;
订单修改与死锁问题
本文探讨了在商品订单修改过程中遇到的死锁问题,并分析了其产生的原因。提出了包括事务优化、资源有序化和功能重构等多种解决方案。
1808

被折叠的 条评论
为什么被折叠?



