前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >事务注解(@Transactional)引起的数据覆盖故障

事务注解(@Transactional)引起的数据覆盖故障

作者头像
普通程序员
发布2019-10-23 14:15:23
6340
发布2019-10-23 14:15:23
举报
文章被收录于专栏:普通程序员普通程序员

最近组织团队内技术培训,刘聪为分享的一个跟事务和写数据库相关的case(bug)很有代表性。用事务,要小心!

一、故障现象

车辆交付履约流程上两个节点(工程项目)A和B, A修改一条数据记录item(工单),然后发消息给B,B也会对item进行修改。

故障现象,有时候(不是必现)感觉A没有成功修改item这条数据,而日志显示A修改成功了数据item!

看一下具体代码实现。下图是工程A代码,3个红框依次动作。

1、开启事务

2、修改工单记录item

3、向下游节点发送mq消息

下图是下游消费mq消息的节点B,红框表示采用JPA技术修改数据记录item

二、原因分析

这个过程总共经历5个步骤,见下图

1、节点A开启一个事务,修改数据表中某条数据item

2、A向B发送mq消息,再做些其他事情,提交事务

3、节点B,消费mq消息

4、节点B读出数据item

5、节点B在内存中修改数据item某些字段,写回数据库

注意到第1、2步骤是在一个事务中。存在一种可能,B节点收到mq消息,执行第4步骤,读取item数据后,步骤1、2的事务才完成提交。由于数据库事务隔离级别,这种情况下,第4步骤读到的数据并不是A节点在第1步写的,已经读到脏数据了。当第5步写回数据的时候,就可能造成老数据覆盖A写的新数据。

这里有两个细分场景

1、第1步、第5步修改同一个字段。这种情况,第4步骤读到脏数据

2、第1步、第5步修改不同字段。第4步读到col2字段的oldvalue,第5步目的是修改col3的值,但是采用jpa或者mybatis的一些默认写法,会把col2的oldvalue更新回数据库。

一般的ORMapping框架利用一个vo对象写数据库记录,没有修改的字段不会更新(代码里并没有改col2的值),但是第4步读取数据后,第1步对数据item进行了修改。这样默认的写库方法,会check记录的变化,然后把col2字段的值更新。这样就出现了旧值覆盖新值的问题。

三、解决办法

1、考虑到实施成本,如果修改不同的字段,不存在竞争关系。只需要在第5步写库的环节指定更新字段就能快速解决这个问题。事实上,生产环境下也是选择的这个方案临时修复。

2、解决办法1显然不够优秀。更好的做法,把第2步发mq消息从事务中拆出来,等第1步操作commit后在发mq消息。这个办法涉及到一些逻辑的梳理(业务代码里会有不少的if……else),代码的改动。这样处理仍然不够完美,第1步执行完了,第2步失败了怎么办?在这里可能需要一些额外的代码工作保证第2步执行成功。

3、如果业务压力不大,也可以考虑从数据库的事务隔离级别方面入手来解决这个问题。

4、业务上,第1步到第5步如果需要强一致,了解一下分布式事务

https://www.jianshu.com/p/16b1baf015e8

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 普通程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档