最近在做一个saas项目,该项目用的是springboot+springdata,持久数据层用的是mogoDB。
因为mogoDB4.0新增了多文档事务支持,再加上项目的业务需求,用mogoDB确实是个不错的选择,但是因为mogoDB的事务功能还不够成熟,采用的是全局一致性事务控制,也就是因为这个全局事务控制的特性,在这个项目上引发了非常严重的问题。
我先解释下什么是全局一致性把。
在mogoDB进入了一个事务中的时候,mogoDB会把事务中的数据操作(sql操作)涉及到的数据库(你没有听错,就是整个库)形成一个快照(就是记录这个事务开始时整个库的数据),所以其他事务在这个库中增删改的数据这个事务无法读到。
这会引发什么问题?
当我在含有@Transactional注解的业务方法serviceA()去调用另一个业务类中(注意是另一个类,因为@Tanscational注解的特性,不同类处在不同的事务中)含有@Transactional的业务方法serviceB(),而两个serviceA()和serviceB()都修改了同一条数据C(方法A的修改在方法B之前),那么此时mogoDB会报WriteConflict 写冲突的异常。因为serviceB()的事务读取不到serviceA()事务中修改的数据。就算把serviceA()中修改C的操作放在执行serviceB()后面,等serviceB()的事务提交了再在serviceA()里修改C,还是会报WriteConflict 写冲突。因为serviceA()事务开始时mogoDB形成了全局一致性快照(4.0特性)。所以不管你把修改C的操作放在serviceA()哪里都是不行的。
如何解决?
目前只找到一种解决方法:
依靠@Tansactional注解的特性,把serviceA()中修改C的操作放到另一个类的方法serviceC()中,然后加上注解
@Transactional(propagation = Propagation.REQUIRES_NEW)
REQUIRES_NEW 这里我就不具体解释了,propagation这个属性是设置事务传播属性,而REQUIRES_NEW是新建一个事务,如果该方法被其他事务方法调用了,则把其他事务挂起,先执行有REQUIRES_NEW的事务。
这时再在serviceA()里调用serviceB()后再去调用serviceC(),修改C就能读到执行serviceB()后的数据了。
但是这样做有一个缺陷,我本来是想要serviceA()回滚后,serviceC()也跟着回滚,但是实际上serviceA()回滚后,serviceC()不会回滚,已经提交了。有取就有舍,唉。