他のバージョンの文書 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

3.4. トランザクション

トランザクション はすべてのデータベースシステムで基礎となる概念です。トランザクションの基本的要点は複数の手順を単一の「全てか無しか」の操作にまとめあげることです。手順の進行途中の状態はほかの動いているトランザクションからは見えません。 そして、なんらかのエラーが引き起こるとトランザクションの完結を防ぐのです。 ですからデータベースはエラーの原因となった手順によってまったく影響されることはありません。

例をあげましょう。ある銀行のデータベースでそこに多数の顧客の口座の残高と支店の総預金残高が記録されているとします。アリスの口座からボブの口座に $100.00 の送金があったことを記録したいとします。ちょっと乱暴に単純化するとこの SQL はつぎのようになります。

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

書かれている SQL コマンドの詳しいことについて今のところ重要でありません。重要な点はどちらかといえばこの単純な操作の目的を果たすための独立した更新手続きが係わっていることです。銀行職員としてはこれらすべての更新が行われるかもしくはまったく行われないのかいずれかの確証が必要です。$100.00 がアリスの口座から引き落されずにボブの口座に振り込まれるようなシステムの不備があってはなりません。一方、$100.00 がボブに振り込まれないでアリスの口座から引き落とされたとしたら、アリスはこの銀号のお得意様ではなくなるでしょうね。操作の途中で一部不都合が発生した場合、結果に影響をあたえるいかなる手続きも実行されないという確証が必要です。更新手続きを トランザクションにグループ化するとその確証が得られます。 あるトランザクションはほかのトランザクションから見て完結するかまったく起こらなかったかという見方から原子的と呼ばれます。

もう一方いったんトランザクションが完結しデータベースシステムが承認したばあいは確実に不変のものとして記録され、たとえ直後にクラッシュが起こったとしても記録は失われないという確証も必要です。 例えばボブが自分の口座から現金を引き落として店舗から立ち去った直後にボブの口座からの引き落とし記録がシステムのクラッシュで消えてしまうことは受け入れられません。 トランザクションが実装されているデータベースではあるトランザクションによるすべての更新がそのトランザクションを完結したと通知をおこなう前に(ディスクなどの)永続的記録装置にログを書き込むことで保証しています。

ほかにもトランザクション実装のデータベースの重要な特性は原子的更新という概念に深く関係していることです。複数のトランザクションが同時に動作しているとき、それぞれのトランザクションは別のトランザクションがおこなっている未完了の変更を見ることができてはなりません。例えば、ひとつのトランザクションがすべての支店の残高を集計する作業に忙しくて、アリスの口座がある支店がアリスの口座からの引き落としを勘定にいれず、ボブの口座がある支店がボブの口座への振り込みを記帳しないとしたら、その逆もありますがどうなりますか。ということはトランザクションはデータベース上での恒久的効果という意味のみならず一連の操作の過程で可視性ということにおいては「すべて」か「無し」かでなければなりません。作業中のトランザクションによる更新はほかのトランザクションからはトランザクションが完結するまで不可視です。 そのトランザクションが完結したその時点でトランザクションが行った更新のすべてがその時点で見えるようになります。

PostgreSQL ではトランザクションを構成する SQL コマンドを BEGINCOMMIT で囲んで設定します。そうすると、この銀行取り引きのトランザクションの実際はつぎのようになります。

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- etc etc
COMMIT;

トランザクション処理の途中でコミットを行わない(アリスの口座残高が足りなかったような場合)と判断した場合は COMMIT ではなく ROLLBACK を使用して行なったすべての更新を破棄します。

PostgreSQL は実際すべての SQL 命令文をトランザクション内で実行するようになっています。BEGIN を発行しないでもそれぞれの命令文は暗黙的に BEGIN がついているとみなし(成功すれば)COMMIT で囲まれているものとします。BEGINCOMMIT で囲まれた命令文のグループは トランザクションブロックと呼ばれることもあります。

注意: いくつかのクライアントライブラリは自動的に BEGINCOMMIT コマンドを発行し、警告無しにトランザクションブロックが有効になるようにします。使用しているインタフェースのドキュメントで確認してください。

セーブポイントを使用することでより粒度を細かくトランザクション内で命令文を制御することが可能になります。セーブポイントはトランザクションを構成するある部分を選択的に破棄する一方破棄されない残りの部分はコミットされます。SAVEPOINT コマンドでセーブポイントを定義したあと必要であれば ROLLBACK TO コマンドによりセーブポイントまでロールバックできます。定義されたセーブポイントとロールバックするポイントとの間の全てのトランザクションデータベースの変更は破棄されますが、セーブポイント以前の変更は保持されます。

セーブポイントまでロールバックした後更にセーブポイントの定義が繰り返されますのでロールバックのポイント定義は何回でもできます。逆に再度ロールバックする特定のポイントが必要ないのであれば解除しシステムリソースを多少とも開放する事ができます。注意する点はセーブポイントを解除したりセーブポイントにロールバックする事は自動的にその後に定義された全てのセーブポイントを解除する事になります。

これら全てはトランザクションブロック内で引き起こる事から他のデータベースセッションからは何も見えません。トランザクションブロックをコミットした場合他のセッションからはコミットされた行為がひとつの単位として見えるようになりますが、ロールバックの行為は決して可視になりません。

銀行のデータベースを思いだしてください。アリスの口座から $100.00 を引き出してボブの口座に振り込むとします。後になってボブではなくウィリーの口座に振り込むべきだったと気がつきました。この場合セーブポイントを次のようにつかいます。

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- おっと、忘れるところだった。ウィリーの口座を使わなければ。
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;

この例はもちろん極端に単純化していますがセーブポイントの使用を通じてトランザクションブロックに対し多くの操作を行えることが判ります。更にはなんらかのエラーでシステムがトランザクションブロックを中断した場合 ROLLBACK TO コマンドがロールバックの完遂を迂回し再開始させるための制御を取り戻す唯一の手段です。