持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 23 天,点击查看活动详情
1 前言
在日常的项目开发过程中,事务操作是经常遇到的,在之前的文章中已经介绍了事务的使用方法以及失效的场景,在本文中将继续分享使用事务的一些经验之谈,希望在解决具体的业务问题上能有所帮助。
2 事务操作
在 springboot
单体项目中操作事务,不仅需要开启事务,加上事务注解,更重要的是声明事务管理器,不过关于事务注解的使用,大多数都推荐声明式事务,就是使用 @Transactional
注解来实现事务的操作,但是开发者关于这个注解的认知和使用水平不尽相同,而且在实际开发中可能会出现代码复用的情况,极大地增加了事务失效的可能。因此在实际的操作中,在需要使用事务的场景中,还是使用编程式事务比较好点儿,编程式事务在何种情况下都会生效。
如下图所示,是一个账户入账操作的事务举例,使用了一个 TransactionTemplate
事务模板, execute 传入的是一个 TransactionCallback
回调函数, status 是一个 TransactionStatus
对象,在业务方法内部是一个 try-catch 结构,如果在业务执行过程中遇到了异常信息,则直接 status.setRollbackOnly()
即回滚事务,最后抛出异常信息,事务模板返回的是一个 Object 对象,可能是返回成功的字符串,也可能是一个异常信息,所以需要根据具体的类型进行判断后,在进行业务处理。
3 事务的注意事项
在事务操作过程中,如果需要锁住某些信息,比如是账户或者用户信息,可以使用 select ... for update
操作,这样就结合事务操作就可以实现业务功能,如果是批量操作资源时进行加锁,一定要注意加锁的顺序,在传入加锁的 id 时,一定要按照按照 id 的顺序进行操作,否则极容易产生死锁的问题。
此外事务的操作方法内部不能有耗时的操作,比如是 io 操作或者 http 请求,这些都是耗时较长的操作,对于数据库事务来说,就会导致资源长时间得不到释放,因此会导致超时或者业务接口性能下降,因此在事务内不能有耗时操作,尽量移动到事务开始前的数据准备阶段或者是事务操作之后进行异步处理,以降低事务的执行时间提高数据库的性能。
另外在事务的内部,最好只有一些增删改的操作,需要把不必要的操作都放在事务的外部进行操作,以减少事务的耗时,在上图的入账接口中就是把查询数据校验的接口放在了事务开始之前,在修改账户余额时,也仅仅是根据账户 id 修改了账户余额信息。这样的操作简单简约,更清楚的理解业务逻辑。
4 总结
在本文中主要讲述了事务操作的注意事项,主要是推荐使用编程式事务来解决业务问题,另外也提出了一些经验之谈来减少事务内部的操作耗时,尽量的减少事务操作的时间,以提高数据库的操作性能。