在Spring中进行事务管理非常简单,只需要在方法上加上注解@Transactional
,Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务与@Transactional
划上了等号,只要有数据库相关操作就直接给方法加上@Transactional
注解。
不瞒你说,我之前也一直是这样,直到使用@Transactional
导致了一次生产事故,而那次生产事故还导致我当月绩效被打了D...
@Transactional
导致的生产事故19年在公司做了一个内部报销的项目,有这样一个业务逻辑:
1、员工加班打车可以通过滴滴出行企业版直接打车,第二天打车费用可以直接同步到我们的报销平台
2、员工可以在报销平台勾选自己打车费用并创建一张报销单进行报销,创建报销单的同时会创建一条审批流(统一流程平台)让领导审批
当时创建报销单的代码是这么写的:
/**
* 保存报销单并创建工作流
*/
@Transactional(rollbackFor = Exception.class)
public void save(RequestBillDTO requestBillDTO){
//调用流程HTTP接口创建工作流
workflowUtil.createFlow("BILL",requestBillDTO);
//转换DTO对象
RequestBill requestBill = JkMappingUtils.convert(requestBillDTO, RequestBill.class);
requestBillDao.save(requestBill);
//保存明细表
requestDetailDao.save(requestBill.getDetail())
}
代码非常简单也很 “优雅”,先通过http接口调用工作流引擎创建审批流,然后保存报销单,而为了保证操作的事务,在整个方法上加上了@Transactional
注解(仔细想想,这样真的能保证事务吗?)。
报销项目属于公司内部项目,本身是没什么高并发的,系统也一直稳定运行着。
在年末的一天下午(前几天刚好下了大雪,打车的人特别多),公司发通知邮件说年度报销窗口即将关闭,需要尽快将未报销的费用报销掉,而刚好那天工作流引擎在进行安全加固。
收到邮件后报销的人开始逐渐增多,在接近下班的时候到达顶峰,此时报销系统开始出现了故障:数据库监控平台一直收到告警短信,数据库连接不足,出现大量死锁;日志显示调用流程引擎接口出现大量超时;同时一直提示CannotGetJdbcConnectionException
,数据库连接池连接占满。
在发生故障后,我们尝试过杀掉死锁进程,也进行过暴力重启,只是不到10分钟故障再次出现,收到大量电话投诉。 最后没办法只能向全员发送停机维护邮件并发送故障报告,而后,绩效被打了个D,惨...。
通过对日志的分析我们很容易就可以定位到故障原因就是保存报销单的save()方法,而罪魁祸首就是那个@Transactional
注解。
我们知道@Transactional
注解,是使用 AOP 实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。
当 Spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,对于@Transactional注解包裹的整个方法都是使用同一个connection连接。如果我们出现了耗时的操作,比如第三方接口调用,业务逻辑复杂,大批量数据处理等就会导致我们我们占用这个connection的时间会很长,数据库连接一直被占用不释放。一旦类似操作过多,就会导致数据库连接池耗尽。
在一个事务中执行RPC操作导致数据库连接池撑爆属于是典型的长事务问题,类似的操作还有在事务中进行大量数据查询,业务规则处理等...
何为长事务?
顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务。
长事务会引发哪些问题?
长事务引发的常见危害有:
既然知道了长事务的危害,那如何在开发中避免出现长事务问题呢?
很明显,解决长事务的宗旨就是 对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度。
既然提到了事务的颗粒度,我们就先回顾一下Spring进行事务管理的方式。
声明式事务
首先我们要知道,通过在方法上使用@Transactional
注解进行事务管理的操作叫声明式事务 。
使用声明式事务的优点 很明显,就是使用很简单,可以自动帮我们进行事务的开启、提交以及回滚等操作。使用这种方式,程序员只需要关注业务逻辑就可以了。
声明式事务有一个最大的缺点,就是事务的颗粒度是整个方法,无法进行精细化控制。
与声明式事务对应的就是编程式事务。
基于底层的API,开发者在代码中手动的管理事务的开启、提交、回滚等操作。在spring项目中可以使用TransactionTemplate
类的对象,手动控制事务。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(RequestBill requestBill) {
transactionTemplate.execute(transactionStatus -> {
requestBillDao.save(requestBill);
//保存明细表
requestDetailDao.save(requestBill.getDetail());
return Boolean.TRUE;
});
}
使用编程式事务最大的好处就是可以精细化控制事务范围。
所以避免长事务最简单的方法就是不要使用声明式事务@Transactional
,而是使用编程式事务手动控制事务范围。
有的同学会说,@Transactional
使用这么简单,有没有办法既可以使用@Transactional
,又能避免产生长事务?
那就需要对方法进行拆分,将不需要事务管理的逻辑与事务操作分开:
@Service
public class OrderService{
public void createOrder(OrderCreateDTO createDTO){
query();
validate();
saveData(createDTO);
}
//事务操作
@Transactional(rollbackFor = Throwable.class)
public void saveData(OrderCreateDTO createDTO){
orderDao.insert(createDTO);
}
}
query()
与validate()
不需要事务,我们将其与事务方法saveData()
拆开。
当然,这种拆分会命中使用@Transactional
注解时事务不生效的经典场景,很多新手非常容易犯这个错误。@Transactional
注解的声明式事务是通过spring aop起作用的,而spring aop需要生成代理对象,直接在同一个类中方法调用使用的还是原始对象,事务不生效。其他几个常见的事务不生效的场景为:
“- @Transactional 应用在非 public 修饰的方法上
- @Transactional 注解属性 propagation 设置错误
- @Transactional 注解属性 rollbackFor 设置错误
- 同一个类中方法调用,导致@Transactional失效
- 异常被catch捕获导致@Transactional失效
”
正确的拆分方法应该使用下面两种:
1 . 可以将方法放入另一个类,如新增 manager层
,通过spring注入,这样符合了在对象之间调用的条件。
@Service
public class OrderService{
@Autowired
private OrderManager orderManager;
public void createOrder(OrderCreateDTO createDTO){
query();
validate();
orderManager.saveData(createDTO);
}
}
@Service
public class OrderManager{
@Autowired
private OrderDao orderDao;
@Transactional(rollbackFor = Throwable.class)
public void saveData(OrderCreateDTO createDTO){
orderDao.saveData(createDTO);
}
}
2 . 启动类添加@EnableAspectJAutoProxy(exposeProxy = true)
,方法内使用AopContext.currentProxy()
获得代理类,使用事务。
SpringBootApplication.java
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}
OrderService.java
public void createOrder(OrderCreateDTO createDTO){
OrderService orderService = (OrderService)AopContext.currentProxy();
orderService.saveData(createDTO);
}
使用@Transactional
注解在开发时确实很方便,但是稍微不注意就可能出现长事务问题。所以对于复杂业务逻辑,我这里更建议你使用编程式事务来管理事务,当然,如果你非要使用@Transactional
,可以根据上文提到的两种方案进行方法拆分。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/NBsdhzk9Rx7-SSnCIVcPtg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。