无论你使用的是哪一种支持事务ACID的数据库,比如Oracle server, MS SQL SERVER, MySQL, PostgreSQL 等等,当你执行完了一个事务,发出commit语句后,如果得到了返回结果,那么这个结果可以告诉你这个事务是否提交成功。但是如果发出commit语句后,数据库连接断开了,或者阻塞得不到返回值,(最终可能因为各种超时而返回),你怎么确定你的事务到底提交成功没有?
这是用户必须要解决的问题,但是也是一个对任何数据库管理系统都没有什么完备和可靠的解决方案的问题。根据数据库事务处理理论,一个数据库系统需要确保如果它告知用户一个事务提交成功,那么该事务的改动必须持久保存。主流数据库都可以保证这一点。但是在各种灾难情况下无法告知用户某个事务是否成功提交。用户可以做的只能是查数据,比如上例的事务中你插入了一行,那么断连后你重连DB然后select那行看在不在。但是这么做有两个问题。
问题1:
你插入那行可能在这个时间窗口中已经被另一个事务修改或者删除了,你找不到那行不代表那个事务提交就失败了--- 它可能是成功了,也可能失败了。总的来说这种情况下,没有100%可靠的并且标准的方法,让每种事务型数据库提供给用户去查,在这种断连情况下,自己的事务到底提交成功没有。
问题2:
你检查之时那个提交还在进行中,所以你没有查到。但是之后不久那个事务就提交成功了。这种情况下如果你依据这个检查结果决定重复执行那个事务那么可能出现严重的错误。想象一下转账的情形。并且这个问题完全无法完美解决。当数据库系统负载很重时,处理commit的时耗比平时更长很多,此时也是客户端发出commit得不到响应容易出现的时候。所以用户用查数据的方法判断是否提交成功的话,更容易得到错误的结果,认为一个事务没有提交成功但是其实它提交成功了。这个问题基本无解。
这两个问题在单机数据库系统和分布式数据库系统中都存在。到目前为止这个问题仍然没有一个标准的并且100%可靠的手段解决这个问题。下面我列出一些解决问题1 的备选方案以及它们的缺陷。
我们先讨论一下单机的事务数据库的情形。理论上我们可以使用事务redo日志查找一个事务是否提交成功,但是redo日志是定期清除的,在使用的系统中,redo日志不可能一直增长,总是得清除的。那么如果用户的检查是在清除之后那么就无法得到正确的结果。如果我们把每个事务是否提交成功的结果记录到另一个地方,比如一个表或者日志文件,那么那个实体也不得不定期清除。另外由于问题2的存在,这个redo日志或者提交记录被写好之前用户就做检查的话,那么会误以为提交失败了。也就是说,用户只能在某个特定的但是起始时间不确定的时间段内可以得到准确的结果。
在TDSQL XA中,由于我们记录了commit log,所以用户可以在发出commit语句之后发生断连或者客户端程序crash等问题后,用select语句查找commit
log来确定一个事务是否将会提交成功。不过他只能在一定时间之后(比如断连10秒之后)查,查到就代表一定会提交成功(但是此刻是否已经提交成功仍然是未知的),查不到就表示该事务的提交一定不会成功。不过由于commit
log也是会定期清除的,所以用户在30分钟后才查的话,就无法得到可靠的结果了。这里还需要处理一些特殊情况,本文不再赘述,但是总的来说TDSQL XA的分布式事务处理机制在大量用户实际使用验证后证明是非常可靠的。
领取专属 10元无门槛券
私享最新 技术干货