背景
经常听同事信誓旦旦的说使用通过sping事务操作数据库可以保证绝对的安全,但是本文想抬杠一下,使用spring事务虽然可以保证对于数据库来说事务要么执行要么回滚,但是对于应用程序,也就是spring所在应用进程来说,极端情况下它并不知道事务的执行结果,也就是它没法确认事务到底是成功了还是失败了。这么说是不是听上去很奇怪?为什么sping所在的应用程序没法确认事务是否成功执行还是失败回滚的呢?
真相追踪
我们假设我们的应用程序代码如下:
private boolean doTrasaction(){
try{
begin trasaction;
....
commit; // 提交事务
}catch(Exception e){
rollback(); // 回滚事务
throw e;
}
}
我们假设使用spring事务执行如上所示的代码,正好在commit提交事务的时候网络中断,如下图所示
我们可以确认数据库确实完整执行了所提交的事务,但是假设数据返回的commit响应丢失,那么我们应用程序此时是什么状态呢?
可以确认的是我们应用程序肯定会收到一个网络中断的异常,那么问题就来了,网络中断异常的背后到底是对应事务已经回滚还是事务已经成功执行?事实上,此时对于应用程序来说它并不知道数据库事务的执行情况,因为不论此时我们是假设事务成功执行还是回滚都不正确,单纯通过应用程序本身已经没法判断事务的执行状态。
小结
那这种情况下应用程序应该怎么处理呢?正确的处理要分几种情况:
1.首先简单的重试事务肯定不是安全的,因为对于数据库来说重试意味着一个新的事务重新执行,比如扣款的话已经扣了两次
2.保险的做法是重新判断此时数据库记录的状态来决定是重试还是做其他的操作
其实这种情况下就又涉及到端到端的一致性保证以及幂等性等话题了。