我发现,当引发异常时(即使异常被误吞!),postgres PDO会自动回滚先前的更改.示例(用伪代码):
$transaction->begin();
try {
$manager->insert("INSERT ...");
try {
$manager->exec("A QUERY BREAKING SOME DB CONSTRAINT LIKE A UNIQUE INDEX ...");
} catch (\Exception $ex) {
// IT IS CAUGHT AND SWALLOWED!
}
$transaction->commit();
} catch (Exception $ex) {
$transaction->rollback(); // THIS CLEARLY DOES NOT RUN!
}
谁能对此事提供一些启示?可以改变这种荒谬的行为吗?我想自己执行回滚,而当pg认为合适时,不要让pg做.
解决方法:
这不是PDO的错,而是Postgresql事务管理所固有的.看到:
> How can I tell PostgreSQL not to abort the whole transaction when a single constraint has failed?
> Can I ask Postgresql to ignore errors within a transaction
> Rollback after error in transaction
Postgresql不会回滚该事务,但会将其设置为中止状态,在该状态下它只能回滚,并且除ROLLBACK之外的所有语句均报告错误:
ERROR: current transaction is aborted, commands ignored until end of transaction block
(我很惊讶没有在官方文档中找到它;我想我需要写一个补丁来改善这一点.)
所以.当您尝试/捕获并吞下PDO中的异常时,您是在捕获PHP端异常,但是您并没有改变Postgresql事务处于中止状态的事实.
如果您希望能够吞下异常并继续使用事务,则必须在每个可能失败的语句之前输入create a SAVEPOINT
.如果失败,则必须回滚到SAVEPOINT …;.如果成功,则可以释放SAVEPOINT …;.这在数据库上增加了用于事务管理的额外开销,增加了往返次数,并更快地烧录了事务ID(这意味着Postgresql必须做更多的后台清理工作).
通常最好改而设计您的sql,这样在正常情况下它不会失败.例如,您可以验证客户端的大多数约束,将服务器端的约束视为第二级保证,同时捕获客户端的大多数错误.
如果不切实际,请使您的应用程序具有容错能力,以便它可以重试失败的事务.有时无论如何这都是必要的-例如,您通常不能使用保存点从死锁事务中止或序列化失败中恢复.保持容易发生故障的事务尽可能短,仅执行所需的最少工作,这也很有用,因此您无需再跟踪和重复.
因此:在可能的情况下,在重试循环中运行容易发生故障的数据库代码,而不是吞下异常.确保您的代码记录了发生错误时重试整个事务的信息,而不仅仅是最新的语句.
请记住,任何事务都可能失败:DBA可能会重新启动数据库以应用补丁,由于cron作业失控等原因,系统可能会耗尽RAM.
为您提供至少使用PDO异常和处理异常的建议-您已经领先于大多数开发人员.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。