DBMSにはデータを安全にかつ効率よく処理するための様々な機能が用意されています。その中で、障害が発生してもデータに矛盾が起こらないようにする機能をトランザクション管理機能と言います。
本記事ではまず、トランザクションとはどのようなものなのかは考え、次にトランザクションを管理する機能のひとつであるコミットメント制御について見ていきます。
トランザクションとは
トランザクションとは、ユーザーから見た一連の処理のまとまりのことです。1つのトランザクションの中で、テーブルのデータを複数回更新する場合、トランザクションは「テーブルのデータに対する更新の単位」とも言えます。
トランザクションの説明でよく使われる銀行振り込みの例で考えてみます。
山田さんが佐藤さんの口座に5万円振り込む場合、山田さんの口座の預金額を5万円減らし、佐藤さんの預金額を5万円増やすことになります。
この更新をSQLにすると以下の2つになります。
UPDATE 預金口座 SET 預金額 = 預金額 - 50000 WHERE 口座番号 = 山田さんの口座番号; UPDATE 預金口座 SET 預金額 = 預金額 + 50000 WHERE 口座番号 = 佐藤さんの口座番号;
もし、ひとつ目のSQLが実行された直後にシステム障害などが発生し、ふたつ目のSQLが実行されなかった場合、山田さんの預金額だけが減り、佐藤さんの口座への振り込み処理が成立しません。これは一大事です。
このふたつのSQLは、必ずセットで行われる必要があります。このように、セットで実行されるべき1つ以上の更新処理の集まりは、必ずトランザクションとしてひとまとめに扱う必要があるのです。
トランザクションの開始・終了
トランザクションの開始と終了は以下の構文で指定します。
- トランザクションの開始と終了
-
トランザクション開始文;
DML文1;
DML文2;
DML文3;
トランザクション終了文(COMMIT または ROLLBACK);
上記構文のように、「トランザクション開始文」と「トランザクション終了文」で更新を行うDML文(INSERT、UPDATE、DELETE文)を囲うことになります。
この「トランザクション開始文」はDBMS製品によって異なります。
PostgreSQL、SQL Server
BEGIN TRANSACTION
MySQL
START TRANSACTION
OracleDB、DB2
なし
例えば、PostgreSQLでトランザクションを記述する場合は以下のようになります。(※説明をわかりやすくするため列名は日本語で記述しています。)
BEGIN TRANSACTION; UPDATE 預金口座 SET 預金額 = 預金額 - 50000 WHERE 口座番号 = 山田さんの口座番号; UPDATE 預金口座 SET 預金額 = 預金額 + 50000 WHERE 口座番号 = 佐藤さんの口座番号; COMMIT;
ここまでで疑問が出てくると思います。なぜ、トランザクション開始文は製品ごとにバラバラなのでしょう。そもそも開始文が存在しないDBMSもあります。SQLの標準規格は標準SQLとして定められているはずです。
これは標準SQLにおいてトランザクションを開始する文ははっきり定義されていないためです。この理由については後述することにし、とりあえず先に進めます。
なお、トランザクション終了文はCOMMITとROLLBACKの2種類しかなく、すべてのDBMS製品で共通です。
コミットとロールバック
トランザクション内のすべての処理が実行されたときに、その更新結果を確定することをコミットと言います。また、トランザクションの途中に何らかのエラーが発生した場合に、処理を取り消し、トランザクション開始以前の状態に戻すことをロールバックと言います。
COMMITは、その名のとおり、更新結果を確定するコミットを行う、つまり、データベースに変更を反映し、トランザクションを終了するためのコマンドです。
一度コミットしたら、もうトランザクションの開始前の状態に戻すことはできません。万が一、間違えた変更を含むトランザクションをコミットしてしまうと、データを再登録するなどの面倒な手順を踏むことになります。DBバックアップを取っていなかった場合など、最悪、データを復元できないケースもあるので、十分に注意する必要があります。
対して、ROLLBACKは、トランザクションに含まれていた処理による変更をすべて破棄して、トランザクションを終了するコマンドです。ロールバックしたら、データベースの状態はトランザクションを開始する前の状態に戻ります。
トランザクションが開始するタイミング
標準SQLにおいてトランザクションを開始する文ははっきり定義されおらず、開始文もDBMS製品によってバラバラであることを前述のとおりです。
標準SQLガイドにはこの点について次のように記載されています。
「また、トランザクションが必ず暗黙的に開始される点にも注意してほしい。”BEGIN TRANSACTION”といった、明示的な開始ステートメントは存在しない。」
この記載のとおり、明示的にトランザクション開始文を記述しなくても、暗黙的に開始されるということです。OracleやDB2といったDBMS製品がトランザクション開始文を用意していないのもこのためです。
では、暗黙的に開始されるといっても、それは具体的にいつなのでしょうか。たいていの場合、データベースへ接続した時点(もしくはデータベースへの接続後、最初にSQLが実行された時点)で開始されます。
このように、コマンドを使わずに暗黙的にトランザクションが開始された場合、そのトランザクションはどのように区切られるかというと、以下の2つのパターンが挙げられます。
①「1つのSQL文で1つのトランザクション」というルールが適用される(自動コミットモード)
②ユーザーがCOMMITまたはROLLBACKを実行するまでが1つのトランザクションとみなされる。
一般的なDBMSでは、設定でどちらのモードも選択可能になっています。PostgreSQLやMySQLのデフォルトは前者の自動コミットモードであり、OracleDBなどは後者がデフォルト設定となっています。
自動コミットモードの場合のDMLは以下のように1文ずつがトランザクション開始文、終了文で囲まれているイメージになります。
BEGIN TRANSACTION; UPDATE 預金口座 SET 預金額 = 預金額 - 50000 WHERE 口座番号 = 山田さんの口座番号; COMMIT; BEGIN TRANSACTION; UPDATE 預金口座 SET 預金額 = 預金額 + 50000 WHERE 口座番号 = 佐藤さんの口座番号; COMMIT;
ただ、この2つのSQLは1セットとしなければいけません。そのような場合は明示的にBEGIN TRANSACTIONを記述します。
一方、後者の設定の場合、ユーザーが自分でCOMMITもしくはROLLBACKコマンドを発行するまで、トランザクションが終わることはありません。