我们经常用spring进行开发,在@Service标注的实体类的方法中标注@Transactional就好了,但其实这样会有很有问题,常见的如下:
1 事务的消失 比如Controller层调用了Service层的没有事务的方法,这个没有事务的方法又调用了有事务的方法,最终的效果其实没有事务。这个是因为AOP的原因导致方法没有被拦截。 粗暴的解决办法就是@Transactional标注在Service类上,所有的方法都有事务。好一点的办法就是在对需要的加事务的方法加上@Transactional,在Controller层直接调用加了事务的方法。
2 事务未回滚 @Transactional默认是运行时异常才会滚,所以发生了非运行时异常,是不会回滚的。解决办法 @Transactional(rollBackFor=Exception.class),只要发生异常,就会滚。
3 更新丢失的问题 比如两个事务分别取出了某一行的数据,根据某个字段取出的值和业务逻辑,进行了一系列的计算,然后更新该字段的值,就会导致这个问题。 比如mysql,默认事务隔离级别是可重复读,但这个问题还是会存在。因为innodb存储引擎采用了一致性非锁定读,用到的技术是MVCC。解决办法:可以在查询语句开始写上SELECT ... FOR UPDATE; 就会对该行加独占锁,其余的读写均被阻塞,直到该事务被提交。这种推并发性的影响比较大,我估计只有在强事务的地方会用到吧,比如金融,银行等地方。 后面看到更好的办法,比如在RabbitMQ中,使用发布者确认机制,保证消息发送成功。或者使用发送的channel是mandatory的,这样当消息不可路由的时候,会通知发送方。美中不足的是,这些过程是异步的。 也可以采用RabbitMQ的事务发送,但要确保只有一个队列绑定于某个交换机。另外,高可用性队列也不错,但是需要集群环境。可能没有完美的解决办法,只有适合业务的解决办法吧。
4 跟第三方中间件集成,导致不一致 比如有个方法中,包含向mq发送消息,以及处理自己的业务逻辑,并更新数据库。不论是先发消息再处理业务,还是先处理也许再发消息,都会造成不一致的情况。这个解决比较复杂,可以简述为消息的正向流程和消息的反向流程。正向流程确保了极大概率数据是一致的,反向流程用于确定业务是不是成功,是一种补偿机制。 正向流程:先发送消息,消息中间件将消息保存,标记为未处理,业务方收到中间件返回的消息。继续处理业务,业务处理完毕,再次发送消息给消息中间件,中间件将消息标记为已经处理,并返回消息。反向处理:对于网络,各类问题导致中间件无法确定某个消息是否处理成功,会轮询业务方,并最终将消息的状态更改。 至此,完成了数据的一致性,消息中间件可以将消息分发给消费方进行处理了。对于小的业务量,这么做得不偿失,可以采用先处理业务,再发消息的模式。容许部分的不一致性。
5 分布式开发的数据最终一致性 可以使用XA进行事务处理,这是强事务的,但是数据库几乎是串行化的工作方式,并且有2个阶段都要prepare,因此效率不高。可以参考CAP理论,只能满足其中的两条,而对另一个进行一定的补偿。一般大家都采用AP,用补偿机制来确保数据的最终一致性。这个我暂时没有深入了解,可以用mq做一个中间者。
网友评论