事务可能会失败.死锁是一个失败的情况,你可以在序列化级别中有更多的失败.交易隔离问题是一场恶梦.试图避免失败是我认为的坏方法.
我认为任何写得很好的交易代码都应该有效地为失败的交易做准备.
正如您已经看到录制查询并重播它们不是一个解决方案,当您重新启动事务时,数据库已移动.如果这是一个有效的解决方案,那么SQL引擎一定会为你做的.对我而言,规则是:
>重做您在交易中的所有阅读(您在外部读取的任何数据可能已更改)
>从以前的尝试中抛出所有内容,如果您在事务之外写入的东西(日志,LDAP,SGBD之外的任何内容),则应该由于回滚而被取消
>重做所有事实:-)
这意味着重试循环.
所以你有你的try / catch块里面的事务.您需要添加一个可能3次尝试的while循环,如果代码的提交部分成功,则保留while循环.如果在3次重试之后,事务仍然失败,然后向用户启动异常 – 以便您不要尝试无限次的重试循环,事实上您可能会遇到一个非常大的问题.请注意,您应该以不同的方式处理SQL错误和锁定或可序列化异常. 3是一个任意数字,你可以尝试更多的尝试次数.
这可能会给出这样的东西:
$retry=0;
$notdone=TRUE;
while( $notdone && $retry<3 ) {
try {
$transaction->begin();
do_all_the_transaction_stuff();
$transaction->commit();
$notdone=FALSE;
} catch( Exception $e ) {
// here we could differentiate basic SQL errors and deadlock/serializable errors
$transaction->rollback();
undo_all_non_datatbase_stuff();
$retry++;
}
}
if( 3 == $retry ) {
throw new Exception("Try later, sorry, too much guys other there, or it's not your day.");
}
这意味着所有的东西(读,写,fonctionnal的东西)必须包含在$do_all_the_transaction_stuff()中.实施事务管理代码在控制器中,即高级应用程序功能主代码,不分为几个低级数据库访问模型对象.