正文
最后没办法只能向全员发送停机维护邮件并发送故障报告,而后,绩效被打了个 D,惨……。
2. 事故原因分析
通过对日志的分析我们很容易就可以定位到故障原因就是保存报销单的 save() 方法,而罪魁祸首就是那个 @Transactional 注解。
我们知道 @Transactional 注解,是使用 AOP 实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。
当 Spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,对于 @Transactional 注解包裹的整个方法都是使用同一个 connection 连接。如果我们出现了耗时的操作,比如第三方接口调用,业务逻辑复杂,大批量数据处理等就会导致我们我们占用这个 connection 的时间会很长,数据库连接一直被占用不释放。一旦类似操作过多,就会导致数据库连接池耗尽。
在一个事务中执行 RPC 操作导致数据库连接池撑爆属于是典型的长事务问题,类似的操作还有在事务中进行大量数据查询,业务规则处理等。
3. 何为长事务?
顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务。
3.1 长事务会引发哪些问题?
长事务引发的常见危害有:
-
-
-
-
3.2 如何避免长事务?
既然知道了长事务的危害,那如何在开发中避免出现长事务问题呢?
很明显,解决长事务的宗旨就是 对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度。
既然提到了事务的颗粒度,我们就先回顾一下 Spring 进行事务管理的方式。
4. 声明式事务
首先我们要知道,通过在方法上使用 @Transactional 注解进行事务管理的操作叫声明式事务 。
使用声明式事务的优点 很明显,就是使用很简单,可以自动帮我们进行事务的开启、提交以及回滚等操作。使用这种方式,程序员只需要关注业务逻辑就可以了。
声明式事务有一个最大的缺点,就是事务的颗粒度是整个方法,无法进行精细化控制。
与声明式事务对应的就是
编程式事务
。
基于底层的 API,开发者在代码中手动的管理事务的开启、提交、回滚等操作。在 Spring 项目中可以使用 TransactionTemplate 类的对象,手动控制事务。