お世話になります。
現在、複数のデータベース定義を一度に変更するパッチをPHPで書いています。
たとえばtest1,test2,test3というデータベース(テーブル定義は同じ)があった場合、
ひとつずつデータベースに接続してSQLを実行して変更していくのですが、
test3で障害が発生したときtest1,test2の変更をロールバックしたいです。
test1,test2,test3はあくまで例で、データベースは顧客数分あります(顧客毎にDBを分ける設計)。
当然、保存されているデータはデータベース毎に異なりますが、新機能追加にあたり、
データベース定義が変更されることになりました。
手動で変更をかけると人的ミスが起こりうるのでパッチを書くことになった、という経緯があります。
調べたところ、データベースをまたがったトランザクションの例はなかなか
出てこないのですがPHPで実装可能でしょうか?
最初に書いたコードを記載します。
PHP
1 // 事前処理は省略 2 3 // 更新用データベース名の格納された配列 4 $corp_list; 5 // トランザクション開始 6 pg_query("BEGIN"); 7 // クエリ実行の成否 8 $db_ret = 0; 9 // 企業数 10 $cnt = 0; 11 12 foreach ($corp_list as $key => $val) { 13 echo "処理開始 => DB名:{$val} \n"; 14 15 // PostgreSQLに接続 16 $con = pg_connect("dbname=$val その他接続情報"); 17 if(!$con){ 18 $db_ret = 1; 19 break; 20 } 21 22 // 更新用SQLの取得、実行 23 $query = 更新用SQL; 24 $result = pg_query($con, $query); 25 if(!$result){ 26 echo "SQL実行ERROR\n"; 27 $db_ret = 1; 28 break; 29 } 30 31 echo "処理終了 => OK\n"; 32 // PostgreSQL接続クローズ 33 pg_close($con); 34 $cnt++; 35 } 36 37 // トランザクション終了 0:コミット、0以外:ロールバック 38 if($db_ret == 0) { 39 echo "COMMIT実行\n"; 40 pg_query("COMMIT"); 41 }else { 42 echo "ROLLBACK実行\n"; 43 pg_query("ROLLBACK"); 44 } 45 46 // DB切断 47 $dbl->disconnect(); 48 echo "処理件数 => {$cnt} \n";
これを実行すると、途中でエラーになってROLLBACKを実行しても変更済みのデータベースが
ロールバックされません。
ご意見・ご指摘いただけますでしょうか。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答4件
0
ベストアンサー
他の方も書かれているようにPHPプログラム側では複数DBに対する同一性を保証する事ができません。
しかしPostgreSQLの「2相コミット(2フェーズコミット)」機能を使えばこれを保証する事ができます。
(KatsumiTanakaさんも書かれている機能です)
やり方は以下の通りです。
(1) beginメソッドによってトランザクションを開始する
(2) insert,update,delete等でデータベースに対する更新を実行する
[2015/10/21 17:32追記]
または、alter等でテーブル定義の更新を実行する
※PostgreSQLはalter等のDDL文にもトランザクションが利用できます
(3) prepare transactionコマンドによって2相コミットを開始する
例)prepare transaction 'transaction_id_00000001';
(4) 対象となるすべてのDBに対して(1)-(3)を実行する
(5) コミット/ロールバックを確定する
・コミットの場合:commit preparedコマンド
例) commit prepared 'transaction_id_00000001';
・ロールバックの場合:rollback preparedコマンド
例) commit prepared 'transaction_id_00000001';
※通常のcommit/rollbackは必要ありません
(6) 対象となるすべてのDBに対して(5)を実行する
※"prepared transaction"を発行されたDBに対しては、
必ずcommit/rollback処理を行うようPG上で保証してください。
以上で2相コミットの処理は終了です。
以下、commitまでの簡単なサンプルです。
begin; insert into employee values (1, 'ariaki', true); insert into employee values (2, 'foobar', true); -- [2015/10/21 17:32追記] -- トランザクション内で以下のようにDDL文が利用できます alter table employee add column joined date not null; prepare transaction 'tran20151021164600'; commit prepared 'tran20151021164600';
DBのトランザクション中に"prepare transaction"をすることで、
そのトランザクションに名前をつけて「永続化」させる事ができます。
永続化された状態では、例えばプログラムを終了してもトランザクションは
残り続けますので、後からidを元にcommit/rollbackを行う事ができます。
※逆に言うと永続化されたデータは必ず後からcommit/rollbackしないと
保持(行ロック)され続けるので注意してください。
※現在有効なトランザクションid一覧は"pg_prepared_xacts"テーブル内を
selectすることで確認できます。
2相コミットは上述の通りトランザクションロックが掛かりますので、
定期的にトランザクション残を確認する等、運用は十分に注意してください。
それぞれのコマンドはPostgreSQLのマニュアルを参照してください。
投稿2015/10/21 07:48
編集2015/10/21 08:33
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/21 08:14

退会済みユーザー
2015/10/21 08:26
2015/10/21 09:51

退会済みユーザー
2015/10/22 04:52
2015/10/27 16:31
2015/10/28 15:27

退会済みユーザー
2015/10/28 15:31
2015/10/28 22:40

退会済みユーザー
2015/10/29 02:24
2015/10/29 03:41

退会済みユーザー
2015/10/29 09:16
2015/10/29 12:33 編集

0
テーブル定義は同じ
ということは、test1~3のDBはバックアップか負荷分散の目的で、全く同じデータを格納しているのでしょうか?
であれば、PHP側で各DBのデータを変更するのではなく、DBのレプリケーション機能を使用してはいかがでしょうか?
http://www.postgresql.jp/document/9.4/html/high-availability.html
投稿2015/10/19 16:05
総合スコア4791
0
やりたいと思われることは、2フェーズコミットというキーワードで検索すれば、
必要な情報が取れると思います
投稿2015/10/19 08:25
総合スコア924
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
try-catchのcatchの中で、test1~test3のロールバック処理を入れれば、
整合性が保たれるかと思います。
ご指摘を受けて以下但し書き追記:
※上記は、コミット時の障害を回避できません。ただし複数DBに対し、コミット時のあらゆる障害に対応できるトランザクションはありませんので、これを許容’(妥協)できない場合は、単一トランザクションで管理できるようなDB設計に修正することオススメします。
投稿2015/10/19 14:44
編集2015/10/19 20:24総合スコア1124
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/19 15:55

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。