トランザクションと排他
データベース(DB)のデータを作成したり変更したりする場合、切り離すことの出来ない一連のデータというものがあります。例えば、現実の世界においては売上伝票明細は売上伝票無しにに存在することはありえません。ところがDB内部では設計上は別のテーブルに存在しているため、データとして売上伝票明細のデータと売上伝票のデータは別々に存在することが可能です。
そこでDBのデータを追加・変更・削除する時、現実の世界とDB内部で不整合が起こらないように、一連のデータの処理をひとまとまりの処理として考える概念があります。それを「トランザクション」といいます(テーブルの種類で出てきた「トランザクションテーブル」のトランザクションとは全く意味が違いますのでご注意下さい)。売上伝票の内容の新規登録の例で言えば、
- 売上伝票データに売上伝票についてのレコードをINSERTする。
- 売上伝票明細データに明細についての複数のレコードをINSERTする。
トランザクションの概念は主に障害発生時に重要になります。1つのトランザクションを処理している最中にDBに異常(停電や誤動作によるDBサービスの停止など)が発生した場合、トランザクション内で処理した全てのデータ操作クエリーをなかったことにして最初からやり直せる状態に戻します。この障害時などにトランザクションを処理する前の状態に戻すことをロールバックといい、逆に正常に終了してトランザクションの処理を確定することをコミットといいます。トランザクションのイメージは下記のようになります。
BEGIN TRAN →トランザクションの開始 INSERT INTO 売上伝票データ ・・・ INSERT INTO 売上伝票明細データ・・・ INSERT INTO 売上伝票明細データ・・・ INSERT INTO 売上伝票明細データ・・・ IF @@ERROR <> 0 ROLLBACK →エラーがあればロールバック ELSE COMMIT →エラーがなければコミットこの場合、エラーがあれば明示的にロールバックし、エラーもなく障害も発生しなければコミットが実行されます。もしクエリー実行途中で障害発生によりコミットが実行されなければ、自動的にロールバックされます。
また、複数のユーザー(DB接続)が同時にDBのデータの操作を行う時、それらが同じデータレコードを一斉に操作するとデータの整合性が保てなくなることがあります。それを防ぐためにこれから操作しようとするデータレコードを他の人が変更できないように排他制御(ロック)します。データレコードをロックすると他のユーザーはそのデータレコードを操作することができません。そして、このロックが有効な期間はデータレコードのロックを実行したトランザクションが終了(コミットまたはロールバック)するまでとなります。
ロックには処理上のリスクがあります。2つ以上のDB接続がお互いに処理しようとしているデータレコードをロックしてしまい、先に進めなくなってしまう状態をデッドロックといいます。必要以上にレコードロックの処理を行うとデータベースサーバに負荷が掛かるだけでなく、デッドロックで処理が動かなくなってしまう要因になるため、ロックは出来るだけ簡素に、そして短時間になるように処理を考えましょう。