トランザクションについて



トランザクションについて

データベースは、複数のユーザーがデータを共有しながら利用するシステムです。また、不慮の障害からデータを守るための仕組みを持たなければなりません。障害に対処するためにRDBMSに用意されているのがトランザクションです。

1.トランザクションとは何か?

SQLではデータベースに対する一連の処理を一つのまとまった作業単位として管理することができます。このような作業単位のことをトランザクションと呼びます。トランザクションの終了後に一連の処理が正しく実行されたどうかによってデータベースに対する変更を確定したり取り消したりすることができます。
通常、SQLでは表に格納されているデータに対して INSERT、UPDATE、DELTEなどのデータ操作で DML文を実行し、様々の変更処理を行います。このような変更処理の中には、相互に関連性が強く、連続的に実行されることによって、意味のある一つの作業単位となるようなものがあります。このように相互に関連する連続的な変更処理を一つの作業単位としてまとめたものがトランザクションです。
一つのトランザクションの中の一連の処理は、最初から最後まで、全て正しく実行されなければなりません。何らかの原因で一度の処理が正しく実行されなかった場合には、作業全体を取り消さなければなりません。全ての処理が正しく実行された場合に初めて、作業全体を確定することができます。
では、どのような場合に、トランザクションが問題となり、必要とされるのでしょうか。また、トランザクションはどのような特性を持つのでしょうか。ここでは、トランザクションの実際の使い方を学ぶ前にRDBMSがトランザクションによって対処しなければならない問題をみたうえで、トランザクションの特性を理解しておきましょう。

2.トランザクションの同時実行制御と障害回復制御

①.同時実行制御

例えば、電車の座席予約システムを考えてみてください。別々の駅からたまたま同じタイミングで、二人の人が座席を予約しようとする場合を想定してみましょう。 もし DATABASE側に何の制約もなければ二人に対して発券処理を行ってしまうでしょう。
このような問題に対処するためにRDBMSは同時実行制御という機能をサポートしています。この機能は、トランザクションの実行中、対象となるデータをロックし、他のユーザーがアクセスできないようにすることによってデータの整合性を維持します。
この例の場合には席の状況を確認するために、データを読み取る処理と、席を予約するためにデータを書き込む処理とを、一つのトランザクションにまとめます。そして、トランザクションの実行中、対象となる席のデータをロックし、他のユーザーが予約情報を書き込むことができないようにします。このような機能を設けることによってデータの整合性が維持されます。

②.障害回復制御

次に、銀行の預金システムを考えてみます。ある会社が本社の口座から営業所の口座に資金を移動しようとする場合を想定してみましょう。この場合には本社の口座の預金残高を減らすという処理と、営業所の口座の預金残高を増やすという処理を連続的に実行しなければなりません。例えばこのような処理の時、本社の口座の資金を減らしたタイミングで何らかの障害が発生した場合に資金がなくなってしまうことになります。このように連続的な処理の途中でシステムに障害が起きるとデータに不整合が生じることがあります。このような問題に対処するためにRDBMSは障害回復制御という機能をサポートしています。この機能はトランザクションが正常に終了した場合にのみデータベースに変更を反映し途中で何らかの障害が発生した場合には反映しないようにすることによってデータの整合性を維持します。

3.トランザクションのACID特性

①.原子性(Atomicity)

トランザクションは、それ以上分離することのできない一つのまとめた作業単位です。そのためトランザクションに含まれる処理は、全て実行されるかもしくは全く実行されないかのどちらかでなければなりません。

②.一貫性(Consistency)

トランザクションで処理されるすべてのデータは、トランザクションの開始前と終了後で整合性を維持し一貫した状態を保持しなければなりません。

③.分離性(Isolation)

データを共有している複数のトランザクションを同時に実行する場合には、それぞれのトランザクションによってデータが変更されていく過程を分離しなければなりません。変更途中の値を複数のトランザクションで共有してはなりません。

④.連続性(Durability)

トランザクションで処理されるデータの状態はトランザクションの終了するまで変化しません。トランザクションの終了時、変更が確定されると変更内容がデータに反映されます。

4.コミットとロールバック

トランザクションに含まれる全ての処理が正しく実行され、トランザクション全体が正常に終了した場合に、作業全体を有効なものと認めた上でトランザクションによる変更処理を確定し、その処理結果をデータベースに反映することをコミットするといいます。一方、トランザクションの実行中、何らかの原因で一部の処理が正しく実行されなかった場合に、作業全体を無効なものとみなした上でトランザクションによる変更処理を取り消し、トランザクションが開始される前の状態にデータベースを戻すことをロールバックするといいます。

5.トランザクションの開始

前述したように SQL 92規格では INSERT、UPDATE、DELTEの いずれかの DML文を最初に発行した時点で、自動的にトランザクションが開始されます。この場合、注意しなければならないことは、トランザクションの対象となる処理は、これら3つの DML文だけだということです。CREATE、ALTER、DROPなどのデータ定義言語(DML)や、GRANT、REVOKEなどのデータ制御言語(DCL)による処理は、トランザクションの対象となりません。これらの SQL 文を発行しても、トランザクションは開始されず従ってロールバックによって処理結果を元に戻すこともできません。

Oracle の場合も基本的に SQL 92規格の場合と同様暗黙的にトランザクションが開始されます。一方 SQL Serverの場合、規定のモードではそれぞれの SQL 文が単独のトランザクションとして実行され自動的にコミットされます。このようなモードのことを自動コミットモードと呼びます。 SQL Serverで複数の SQL 文をトランザクションとしてまとめたい場合には前述したように BEGIN ~ TRANSACTION文を指定し明示的にトランザクション開始します。このようなモードのことを「明示トランザクションモード」と呼びます。この場合には、BEGIN ~ TRANSACTION文を発行した時点でトランザクションが開始されます BEGIN ~ TRANSACTIONの構文は以下の通りです。

BEGIN TRAN [SACTION] <トランザクション名>

6.トランザクションの終了

トランザクションによる変更処理を確定し、その処理結果をデータベースに反映するにはコミット文を実行します。一方、トランザクションによる変更処理を取消し、トランザクションが開始される前の状態にデータベースを戻すにはROLLBACK文を実行します。通常、トランザクションの進行中には、トランザクションに含まれるそれぞれの処理が正しく実行されているかどうかを調べるために何らかのエラーチェックを行います。エラーが発生せずに、全ての処理が正しく実行された場合にはコミット文を実行してトランザクションを確定します。するとトランザクションの処理結果がデータベースに反映されます。一方何らかのエラーが発生し一部の処理が正しく実行されなかった場合には ROLLBACK文を実行しトランザクションを取り消します。するとトランザクションが開始される前の状態にデータベースが戻されます。なおエラーが発生せずに、全ての処理が正しく実行された場合でもROLLBACK文を実行することでトランザクションを取り消すことが可能です。
一般にロールバック文はトランザクションが開始された後、COMMIT文を実行する前に実行しなければなりません。COMMIT文を実行すると、トランザクション処理結果が確定されてしまうので、この時点でロールバックを実行してもトランザクションを取り消すことはできません。また COMMIT文の場合でもロールバックの場合でもトランザクションが終了するとそのトランザクションで設定されていたすべてのロックが解除されます。

①.セーブポイント

通常トランザクションをロールバックすると、トランザクション処理全体が取り消され、トランザクションが開始される前の状態にデータベースが戻されます。しかし、場合によってはトランザクションを部分的にロールバックしてトランザクションの途中地点の状態までデータベースを戻したいこともあります。そのような場合にセーブポイントを使います。
セーブポイントとは、トランザクションの途中地点に定義することでトランザクションを部分的にロールバックする場合に使います。セーブポイントを指定せずに ROLLBACK文を実行するとデータベースの処理状態はトランザクションの開始地点まで戻されます。一方セーブポイントを指定して ROLLBACK文を実行すると、データベースの処理状態はトランザクションの途中のセーブポイントの地点まで戻されます。一方セーブポイントを指定して ROLLBACK文を実行するとデータベースの処理状態はトランザクションの途中のセーブポイントの地点まで戻されます。トランザクションの途中にセーブポイントを定義するには、セーブポイント文を使いますセーブポイント文の構文は以下の通りです。

【Oracle】
SAVEPOINT <セーブポイント名>

【SQL Server】
SAVE TRAN[SACTION] <セーブポイント名>

②.エラーチェック

前述したように、トランザクションの進行中には通常何らかのエラーチェックを行います。エラーが発生せず全ての処理が正しく実行された場合は、コミット文を実行しトランザクションを確定します。何らかのエラーが発生し、処理が正しく実行されなかった場合は、ROLLBACK文を実行しトランザクションを取り消します。
ではどのようにしてエラーチェックを行うのでしょうか。特にストアドプロシージャの中でトランザクション実行する場合には、エラーが発生したかどうかをチェックし、エラーが発生したら対応する処置を行うためのメカニズムを、プログラムの中に記述しておかなければなりません。ここではストアドプロシージャの中でエラーチェックを行う方法を取り上げます。

SQL サーバーのエラー処理

SQL Server はOracle のようにエラー処理を構造化するためのメカニズムがサポートされていません。そのためグローバル変数@@ERRORを使ってエラーを検出します。
@@ERROR には、 最後に実行されてSQL文の結果を示すエラー番号がセットされます。 SQL文が正しく実行された場合には、「0」がセットされます。何らかのエラーが発生した場合には、エラーの内容を示す値がセットされます。@@ERRORの内容は、 SQL 文が実行されるたびにクリアされ、リセットされます。ストアドプロシージャでは、@@ERRORの内容チェックし、エラーを示す値が検出されたら、エラーの内容に応じて対応する例外処理を実行します。

7.ロックと排他制御

データベースは複数のユーザーがデータを共有しながら利用するシステムです。そのため排他制御の考え方が必要になります。排他制御とは変更処理を伴うトランザクションの実行中に、1つのデータに対して、同時に1つの要求しか受け付けないようにする仕組みのことです。あるユーザーがデータを変更する場合には、そのデータをロックし他のユーザがアクセスできないようにしておきます。そして変更処理が終了したらロックを解除し、他のユーザーがそのデータに再びアクセスできるようにします。データをロックする場合にはモード設定することができます。ロックのモードには共有ロックと排他ロックの2種類があります。いずれの場合にもトランザクションが終了するとロックが解除されます。つまり、トランザクションがコミットされるかまたはロールバックされた時点でロックが解除されます。

①.共有ロック

トランザクションがデータを参照する時に設定されるロックのモードです。このモードではロックされているデータを他のトランザクションから参照することはできますが、変更することはできません。

②.排他ロック

トランザクションがデータを変更する時に設定されるロックのモードです。このモードではロックされているデータを他のトランザクションから変更することも参照することもできません。

8.デッドロック

デッドロックとは複数のトランザクションが、互いの処理に必要なデータをロックし合っているために共に永遠の待ち状態になってしまう現象のことです。例えばトランザクションAが、データ1を、トランザクション B がデータ2を入力している状態でトランザクションAがデータ2に、トランザクション B がデータ1にアクセスしようとします。するとデータにはトランザクション B によってロックされているので、トランザクションAはデータ1をロックしたままデータ2のロックが解除されるのを待ちます。一方データ1はトランザクション A によってロックされているのでトランザクション B はデータ2を、ロックしたままデータ1のロックが解除されるのを待ちます。こうして、それぞれが自分のデータをロックしたまま、相手のデータのロックが解除されるのを待ち続けるので、いつまでたっても互いのロックが解除されることはありません。このようなデッドロックは2つのトランザクションの間だけではなく3つ以上のトランザクションが3すくみ、4すくみの状態になって発生することもあります。なので開発者はデッドロックが発生しないようにアプリケーションを設定しなければなりません。一般的な指針としては、できるだけ同じ順序でオブジェクトにアクセスするようにします。そうすることでひとつのデータにはなるべく一つのトランザクションのみアクセスするような仕組みにすることができます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です