Postgresql:PREPARE TRANSACTION

时间:2012-01-21 13:39:39

标签: php postgresql transactions prepared-statement distributed-transactions

我有两个数据库服务器db1和db2。

db1有一个名为tbl_album的表 db2有一个名为tbl_user_album

的表
CREATE TABLE tbl_album
(
id    PRIMARY KEY,
name  varchar(128)
...
);

CREATE TABLE tbl_user_album
(
id          PRIMARY KEY,
album_id    bigint
...
);

现在,如果用户想要创建一个我的PHP代码需要做的专辑:

  • 在db1中创建一条记录并保存其id(主键)
  • 使用第一个语句中保存的记录在db2中创建记录

是否可以将这两个陈述保留在交易中?我也可以使用php解决方案。我的意思是,如果有一个解决方案需要php代码来保留db句柄并在这些句柄上提交或回滚,我就没问题。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:4)

是的,这是可能的,但你真的需要吗?

在你决定这真的必须是两个独立的数据库之前要三思而后行。

如果第二个命令失败,你可以保持两个连接都打开并且ROLLBACK是第一个命令。

如果您确实需要准备好的交易,请继续阅读。

关于你的模式 - 我会在数据库端使用序列生成器和RETURNING子句,只是为了方便。

CREATE TABLE tbl_album (
  id    serial PRIMARY KEY,
  name  varchar(128) UNIQUE,
  ...
);
CREATE TABLE tbl_user_album (
  id          serial PRIMARY KEY,
  album_id    bigint NOT NULL,
  ...
);

现在你需要一些外部粘合剂 - 分布式事务协调器(?) - 以使其正常工作。

诀窍是使用PREPARE TRANSACTION代替COMMIT。然后在两笔交易成功后,使用COMMIT PREPARED

PHP概念验证如下。

警告!此代码缺少严重部分 - 即错误控制。应该抓住$db2中的任何错误,ROLLBACK PREPARED应该$db1执行 如果你没有发现错误,你将$db1留下冻结的交易,这真的非常糟糕。

<?php
$db1 = pg_connect( "dbname=db1" );
$db2 = pg_connect( "dbname=db2" );
$transid = uniqid();

pg_query( $db1, 'BEGIN' );
$result = pg_query( $db1, "INSERT INTO tbl_album(name) VALUES('Absolutely Free') RETURNING id" );
$row = pg_fetch_row($result);
$albumid = $row[0];
pg_query( $db1, "PREPARE TRANSACTION '$transid'" );
if ( pg_query( $db2, "INSERT INTO tbl_user_album(album_id) VALUES($albumid)" ) ) {
    pg_query( $db1, "COMMIT PREPARED '$transid'" );
}
else {
    pg_query( $db1, "ROLLBACK PREPARED '$transid'" );
}
?>

再次 - 在你使用它之前想一想。欧文提出的建议可能更为明智。

哦,还有一个注意事项......要使用此PostgreSQL功能,您需要将max_prepared_transactions配置变量设置为非零值。

答案 1 :(得分:2)

如果您可以从db1中访问db2,那么您可以优化该过程并将其全部保留在事务中。请使用dblinkSQL MED

如果在本地服务器上回滚事务,则通过远程服务器上的dblink执行的操作将回滚。 (如果事务被回滚,这是使更改持久的一种方法。)

但是如果不成功则可以在远程服务器上执行代码,如果本地数据库中的操作首先成功,则只执行代码。如果远程操作失败,您也可以在本地回滚。

另外,使用INSERTRETURNING子句从串行列返回id。

答案 2 :(得分:0)

使用PDO会更容易......

PDO的主要优点是捕获事务中每个单个SQL语句的错误(通过PHP错误行或返回SQL错误消息)。 请参阅pdo.begintransactionpdo.commitpdo.rollbackpdo.error-handling

示例:

$dbh->beginTransaction();
/* Do SQL */
$sth1 = $dbh->exec("CREATE TABLE tbl_album (..)");
$sth2 = $dbh->exec("CREATE TABLE tbl_user_album(..)");
/* Commit the changes */
$dbh->commit();