我有两个数据库服务器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代码需要做的专辑:
是否可以将这两个陈述保留在交易中?我也可以使用php解决方案。我的意思是,如果有一个解决方案需要php代码来保留db句柄并在这些句柄上提交或回滚,我就没问题。
非常感谢任何帮助。
答案 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,那么您可以优化该过程并将其全部保留在事务中。请使用dblink或SQL MED。
如果在本地服务器上回滚事务,则通过远程服务器上的dblink执行的操作将不回滚。 (如果事务被回滚,这是使更改持久的一种方法。)
但是如果不成功则可以在远程服务器上执行代码,如果本地数据库中的操作首先成功,则只执行代码。如果远程操作失败,您也可以在本地回滚。
另外,使用INSERT
的RETURNING
子句从串行列返回id。
答案 2 :(得分:0)
PDO的主要优点是捕获事务中每个单个SQL语句的错误(通过PHP错误行或返回SQL错误消息)。 请参阅pdo.begintransaction,pdo.commit,pdo.rollback和pdo.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();