Postgresql: ПОДГОТОВКА СДЕЛКИ - PullRequest
0 голосов
/ 21 января 2012

У меня есть два сервера БД 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 и сохранить ее идентификатор (первичный ключ)
  • Создать запись в db2, используя еесохранено в первом операторе

Можно ли сохранить эти два оператора в транзакции?Я в порядке с решением PHP тоже.Я имею в виду, что я в порядке, если есть решение, в котором нужен php-код для сохранения дескрипторов db и фиксации или отката этих дескрипторов.

Любая помощь очень ценится.

Ответы [ 3 ]

4 голосов
/ 21 января 2012

Да, это возможно, но вам это действительно нужно?

Подумайте дважды, прежде чем решить, что на самом деле это две отдельные базы данных.

Вы можете просто оставить оба соединения открытыми и ОТКРЫТЬпервая команда в случае сбоя второй.

Если вам действительно нужны подготовленные транзакции, продолжайте чтение.

Что касается вашей схемы - я бы использовал генераторы последовательностей и предложение 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 в ненулевое значение.

2 голосов
/ 21 января 2012

Если вы можете получить доступ к db2 из db1, вы можете оптимизировать процесс и фактически сохранить все это внутри транзакции.Для этого используйте dblink или SQL MED .

Если вы откатите транзакцию на локальном сервере, то, что было сделано через dblink на удаленном сервере, будет не откат.(Это один из способов сделать изменения постоянными, даже , если транзакция откатывается.)

Но вы можете выполнить код на удаленном сервере, который откатывается, если не удастся, и выполнять его только, если операция в локальной базе данных была успешной первой.Если удаленная операция завершится неудачно, вы также можете выполнить откат локально.

Кроме того, используйте выражение RETURNING из INSERT, чтобы вернуть идентификатор из последовательного столбца.

0 голосов
/ 11 мая 2012

Будет проще с PDO ...

Основным преимуществом PDO является захват ошибок (по строке ошибок PHP или возвращаемых сообщений об ошибках SQL) каждой отдельной SQL-статистики вперевод.См. pdo.begintransaction , pdo.commit , pdo.rollback и pdo.error-processing .

Пример:

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