トランザクション

トランザクション

データの追加・更新・削除、SQL 文で言うと「INSERT 文」「UPDATE 文」「DELETE 文」についての処理のまとまりをトランザクションと呼ぶ。

上記 3つのデータ操作文は、お互いに関連をもっていて、連続して実行されることにより、意味のある一つの単位を構成することが少なくない。トランザクションとはこの一連の作業単位のことで、データの整合性を確保するため、またデータの障害復旧といった目的に利用される。

トランザクションの特性

トランザクションは、次の 4つの特性を満たさなければならない。これら 4つの特性は、それぞれの名前の頭文字をとって ACID 特性と呼ばれる。

・原子性 (ATOMICITY)
トランザクションは、それ以上分割することのできない最小の作業単位である。このため、トランザクションを構成する処理の結果がすべて有効になるか、またはすべて無効になるかのいずれかしかない。例えば、あるトランザクションに処理 A と処理 B があるとする。処理 A と処理 B が正しく実行されたときは両方の処理結果が有効になる。しかし、処理 A だけ、または処理 B だけが正しく実行された場合は、両方の処理結果が無効になる。

・一貫性 (CONSISTENCY)
トランザクションで処理されるデータは、実行前と後でデータの整合性を持ち、一貫したデータを確保しなければならない。

・隔離性 (ISOLATION)
処理対象が同じデータである複数のトランザクションを一度に実行する場合は、それぞれのトランザクションは隔離された (独立された) 状態でデータの変更を行わなければならない。

トランザクション A とトランザクション B がデータを共有している場合、トランザクション A で変更中のデータを、トランザクション B で処理することは認められないということです。トランザクション A が終了し、データが確定した後であればトランザクション B でデータを処理することができる。

・持続性 (DURABILITY)
トランザクションで処理されるデータの状態はトランザクションが終了するまで変化しない。トランザクションで処理を変更する SQL 文を実行するが、トランザクションの最後で変更を確定するまでは実際のデータの変更は行われない。これらのトランザクションの特性により、データベースへの同時アクセスを制御したり、障害発生時の処理を制御することができるようになる。

コミットとロールバック

トランザクションによるデータの変更処理が正常に終了した場合に、その変更処理を有効な結果と確定し、データベースに反映することをコミットと呼ぶ。また、トランザクションによるデータの変更処理の途中で何らかの障害が発生した場合に、それまでの変更処理を無効なものとし、トランザクションが実行される前の状態にもどすことをロールバックと呼ぶ。このコミットとロールバックによりトランザクションの ACID 特性が生きていると言える。

/* トランザクションの構文 */
 
(BEGIN WORK ; )       /* PostgreSQL の場合       */
 
INSERT 文 ;           /* トランザクションの開始 */
 
COMMIT ;              /* 処理の確定             */
 
(BEGIN WORK ; )       /* PostgreSQL の場合       */
 
UPDATE 文 ;           /* トランザクションの開始 */
 
ROLLBACK ;            /* 処理の取り消し         */
 
(BEGIN WORK ; )       /* PosgreSQL の場合        */
 
DELETE 文 ;           /* トランザクションの開始 */
 
SAVEPOINT sPOINT ;    /* セーブポイントの設定   */
 
INSERT 文 ;
 
ROLLBACK TO SAVEPOINT sPOINT ;
                      /* セーブポイントへ戻る   */
 
INSERT 文 ;
 
COMMIT ;              /* 処理の確定             */
排他制御

排他制御とは、あるトランザクションが実行中のときに、そのトランザクションが対象としているデータをロックし、他のトランザクションのアクセスを禁止することによって、データの整合性を確保しようとする仕組みである。トランザクションが完了すれば、ロックは解除され、他のトランザクションはデータにアクセスできるようになる。

ロック

排他制御は、前節でも説明したように、データをロックすることにより実現することができる。また、データベースへのアクセスのパフォーマンスなどの観点から、排他制御の方法にはいくつかの方法がある。排他制御の形態は、ロックの形態に左右される。ロックの形態は、そのロックの程度から分類すると共有ロックと排他ロックの 2 種類 (2 モード) がある。これはトランザクションの ACID 特性の一つである隔離性 (ISOLATION) のレベルにかかわってくる。

・共有ロック
トランザクションが SELECT 文によりデータを参照するものであるときのロックのモードである。共有ロックによりデータがロックされている間は、他のトランザクションからデータを参照することはできるが、データの変更はできない。
 
・排他ロック
トランザクションが INSERT 文、UPDATE 文及び DELETE 文であるときのロックのモードである。排他ロックされている間、他のトランザクションからデータの参照も変更もできない。

また、ロックするデータの範囲をロックの粒度またはロックレベルという。ロックするデータの範囲を、テーブル全体にするのか、行全体なのか、特定の列なのかを示すものである。同時実効性を高めるには、ロックの粒度は小さいほうが望ましいと言えるが、トランザクションが衝突する可能性が高くなるという欠点がある。逆に粒度が大きい場合は、トランザクションの衝突の可能性は減るが、同時実効性が低くなってしまう。

実行しようとするトランザクションが共有ロックであるのか、排他ロックであるのかは、後に述べる SET TRANSACTION 文で指定することができる。ロックの粒度も同じ構文で指定することができる。

デッドロック

データのロックにより発生するものに、デッドロックというものがある。これは複数のトランザクションが互いの処理に必要なデータをロックし合っているために、処理不能の状態になってしまうことである。

デッドロックを防ぐには、トランザクションを設計するときに、アクセスするデータの順を決めておくことがよいとされる。つまり、トランザクション A ではデータ A の次にデータ B を処理するという風にするならば、トランザクション B でもデータ A の次にデータ B を処理するという風にすればよいというわけである。

デッドロックを防ぐ方法として、二相ロック方式という考え方もある。これは、あるトランザクションが処理対象としているデータのすべてを、トランザクションの開始時にまとめてロックしてしまい、トランザクションが終了する直前にそのロックをすべて開放するというものである。二相ロック方式によりロックされたデータは、トランザクションで処理中でないデータに対しても、他のトランザクションはデータの変更などの処理をすることができなくなる。