はじめに

正式名は.NETエンタープライズWebアプリケーション開発技術大全Vol.5(トランザクション設計編)と名前長い・・・。

とりあえず、四の五の言わずにまとめるとします。
ただ、SQLServerや.NETに限定した話はまとめるつもりがありません・・・。

第1部 短時間トランザクション処理

第1章 トランザクションと分離レベル

トランザクション処理は何か

トランザクション制御(同時実行制御/排他制御)についてアプリケーション視点でまとめています。

ここでのトランザクションは以下のように定義されています。

物理的には複数のステップから構成されているが、論理的には1つの作業単位として実行しなければならない一連の操作

トランザクションを適切に処理するために求められる4つの特性

  1. 原子性(Atomicity)
    一連の処理が論理的に1つのかたまりとして処理されること
    一連の処理が完全に実施されるか、あるいは完全に取り消されるかのいづれかであること
  2. 一貫性(Consistency)
    トランザクション完了時にすべてのデータが一貫した状態になっていること
  3. 分離性(Isolation)、直列実行性(Serializable)
    複数のトランザクションが同時処理されても、それらが論理的に正しく隔離されて実行されていること
    複数の処理を並列で処理しても、1件づつ直列で処理した結果と同じ結果が得られること
  4. 持続性(Durability)
    トランザクションが終了したら仮にシステム障害などが発生しても、結果が正しく保持され続けること

適切なトランザクション制御が行われなかったっ場合におきうる問題

  1. ロストアップデート
    後勝ちで内容が上書きされてしまい、先の更新内容が失われること
  2. ダーティーリード
    未確定のデータを読み取ってしまうこと
  3. 反復読み取りの不一致
    同一トランザクション内の1回目の読み取りと2回目の読み取りで結果が異なってしまうこと
  4. ファントム読み取り
    複数レコードを同時に処理する場合に起きる問題
    同一トランザクション内の1回目の読み取りになかったレコードが2回目の読み取りで見えてしまうこと
    (1回目と2回目の読み取りの間に他のトランザクションがデータを挿入することで起きる)

トランザクションの分離レベル

データベース内部でACID特性を保障すれば、前述のような問題はおきない。

しかし、データベースにすべてのトランザクション制御(同時実行制御)を行わせると性能上の問題がある。
このため、データベースには同時実行制御機能を調節するためのトランザクション分離レベル(Transaction Isolation Level)という仕組みがある。

このパラメータは調整することで、同時実行制御機能を緩めることができる。
つまり、不適切なトランザクション制御をした場合先の問題がおきうる状態にできる
(当然、不適切なトランザクション制御をしないよう、アプリケーションを工夫するということ)

データベースで設定可能なトランザクション分離レベル(Transaction Isolation Level)

下にいくほど同時実行制御機能が弱められる。

なお、筆者はトランザクション分離レベルは「SERIALIZABLE」から弱めていくという考え方を推奨している。
ちなみに、DBの分離レベルは「READ COMMITTED」が多い。
これはDBをトランザクション制御を考えずに使うと問題がおきうることを意味する。

  1. SERIALIZABLE(完全直列実行)
    • ロストアップデート:発生しない
    • ダーティーリード :発生しない
    • 反復読み取り不一致:発生しない
    • ファントム読み取り:発生しない
  2. REPEATABLE READ(反復読み取り可能)
    • ロストアップデート:発生しない
    • ダーティーリード :発生しない
    • 反復読み取り不一致:発生しない
    • ファントム読み取り:発生しうる
  3. READ COMMITTED(コミット済みデータ読み取り可能)
    • ロストアップデート:発生しうる
    • ダーティーリード :発生しない
    • 反復読み取り不一致:発生しうる
    • ファントム読み取り:発生しうる
  4. READ UNCOMMITTED(未コミットデータ読み取り可能)
    • ロストアップデート:発生しうる
    • ダーティーリード :発生しうる
    • 反復読み取り不一致:発生しうる
    • ファントム読み取り:発生しうる

DBのトランザクション制御機能とトランザクションコミットの関係

DBにトランザクションを宣言したからといって、DBはその処理をすべてコミットするとは限らない。

##ちょっと考えると当たり前のことだが、この意味をちゃんと理解するのは難しい・・・。
##書籍ではこの事実について実験を交えて説明しているので、読んだ方がいい。

実験の内容を要約すると、

5スレッドそれぞれで同じ処理を20回繰り返す並列実行の実験を、
分離レベル「SERIALIZABLE」と「READ COMMITTED」で行っている。

そして、その結果は、
「READ COMMITTED」では

エラーは発生しなかったが40回分の更新結果しか確認できなかった。つまり、更新は成功しているように見えるのに、
データが不整合な状態になる「更新内容の消失(ロストアップデート)」が発生していた。

一方、「SERIALIZABLE」では

25回分の更新結果した確認できなかった。
しかし足りないあとの75回分はACID特性が満たせなくなったため強制的にアボートされ、
「直列実行不能例外」エラーとして失敗した。

つまり、

「READ COMMITTED」はすべてコミットに成功したにもかかわらず、結果に不整合が発生し、
「SERIALIZABLE」ではユーザーからの処理要求をすべて受け付けるわけではないが、
その代わりに更新処理の矛盾やデータの不整合を発生させない。

ということである。

これは要するにデータベースが持つトランザクション制御機能は、

「トランザクションとして宣言された処理をすべてコミットする」というものではなく、
「トランザクションとして宣言された処理の結果がACID特性を満たすこと」を保障するだけで
トランザクションの確実なコミットという結果を保障するものではない。

ということ。そしてこれは、

すべてのトランザクションをコミットさせるためアプリケーション開発者は、
設計を工夫し、DBに対して明示的なロック指定を行う必要がある

ということを意味している。

第2章 (SQLServerの)ロックの管理*1

主要DBのトランザクション制御のための同時実行制御方式

  • ロック方式

    対象リソースにロックを掛け、同時実行に関する問題がないことを確認してからデータの書き込みを行う方式。
    SQLServerやDB2がこの方式。

  • マルチバージョニング方式(時間ドメインアドレッシング方式)

    行われた更新をロールバックセグメントなどで世代管理し、あるトランザクション内での読み取り一貫性を保障する方式。
    Oracleがこの方式。

それぞれの方式にはそれぞれトレードオフがあり、優劣はつけられないよう。 *2

すべてのトランザクションをDBにコミットさせるための設計上・実装上の工夫

  • すべてのトランザクションがリソースに対して同じ順番でアクセスすること
  • 1つのトランザクションが2種類のロックを要求する場合、最初に最も制限の強いロックを獲得すること

主なロックの種類

ロックはあるユーザー/トランザクションのリソースの使用状況を他のユーザー/トランザクションに示すためのもの。
SQLServerの内部で利用される主なロック。

  1. 共有ロック(Share Lock,S-Lock)
    • 読み出し中フラグ、読み出していることを示す。
  2. 更新ロック(Update Lock,U-Lock)
    • 更新予約フラグ、あとで更新する予定であることを示す。
  3. 排他ロック(eXclusive Lock,X-Lock)
    • 書き込み中フラグ、更新していることを示す。

ロックの互換性(Lock Compatibility)

ロックは状況次第で1つのリソースに複数かけることが可能ということ。
以下ロックと同時にかけられるロックの互換性。

  1. 共有ロック(S)
    • 共有ロック:共存可能
    • 更新ロック:共存可能
    • 排他ロック:共存不可
  2. 更新ロック(U)
    • 共有ロック:共存可能
    • 更新ロック:共存不可
    • 排他ロック:共存不可
  3. 排他ロック(X)
    • 共有ロック:共存不可
    • 更新ロック:共存不可
    • 排他ロック:共存不可

上の一覧は次のように言い換えることができる。

  • S + S   ⇒可 :読み出しだけなら何人でも
  • U + S   ⇒可 :更新予約中も読むだけなら
  • U + UX  ⇒不可:更新予約は一人だけ
  • X + SUX ⇒不可:書き込みのときは独り占め

インテントロック

リソースがロックされていることを示すロック。
これはDBMSが粒度の異なるロック(行ロックと表ロック等)を相互に簡単に検知できるようにするためのロック。*3

分離レベルによるロック挙動の変化

(SQLServerでは)分離レベルパラメータの変化により「ロックを保持し続ける期間」と「インデックスキーに対して発生するロックの種類」が変化する。

分離レベルによるロック保持期間の変化

ロックが保持される期間は、分離レベルとロックの種類により変化する。
以下に一例を示す。

  • SERIALIZABLEではリソースに対して掛けられたロックはトランザクションのコミットまたはアボートまで保持される。
  • READ COMMITTEDでは共有ロック(S-Lock)がトランザクションのコミットまで保持されず、SQL実行直後に解放される。
  • READ UNCOMMITTEDではロックを立てずデータを読み取る。

まとめると以下のようになる。

  • SERIALIZABLE分離レベル
    1. 共有ロック(S):トランザクションコミットまで保持
    2. 更新ロック(U):トランザクションコミットまで保持
    3. 排他ロック(V):トランザクションコミットまで保持
  • REPEATABLE READ分離レベル
    1. 共有ロック(S):トランザクションコミットまで保持
    2. 更新ロック(U):トランザクションコミットまで保持
    3. 排他ロック(V):トランザクションコミットまで保持
  • READ COMMITTED分離レベル
    1. 共有ロック(S):SQL文実行直後に解放
    2. 更新ロック(U):トランザクションコミットまで保持
    3. 排他ロック(V):トランザクションコミットまで保持
  • READ UNCOMMITTED分離レベル
    1. 共有ロック(S):ロックを立てない(=他者のロックを無視)
    2. 更新ロック(U):トランザクションコミットまで保持
    3. 排他ロック(V):トランザクションコミットまで保持

分離レベルによるインデックスキーに対するロックの種類の変化

SERIALIZABLEとそれ以外では範囲指定クエリ(ファントムが発生しうるクエリ)を発行した場合に獲得されるインデックスキーロックが異なる。
SERIALIZEABLE分離レベルの場合はインデックスキー範囲ロック(Range-Lock)が獲得されるが、他の分離レベルでは通常のインデックスキーロックが獲得される。

  • SERIALIZABLE
    1. 読み取り:インデックスキー範囲ロック(RangeS-S)
    2. 更新予約:インデックスキー範囲ロック(RangeS-U)
    3. 更新  :インデックスキー範囲ロック(RangeX-X)
  • REPEATABLE READ/READ COMMITTED/READ UNCOMMITTED
    1. 読み取り:インデックスキーロック(S-Lock)
    2. 更新予約:インデックスキーロック(U-Lock)
    3. 更新  :インデックスキーロック(X-Lock)

インデックスキー範囲ロックは、行そのものに加えて前後のキーの間へのデータ挿入や更新、削除を防止する特殊なロックとなる(これがファントムを抑える仕組みとして機能する)。
インデックスキー範囲ロックにも、共有ロック、更新ロック、排他ロックがあり、ロックの互換性も他のロックと同様の動作をする。

ブロック

他のトランザクションのロックによって必要なロックが獲得できない場合にトランザクションが待機状態に入る現象のことをブロックという。

ブロックの振る舞い

(SQLServerでは)ブロックは内部的に単純な待ちとして処理され、ロックの解放を待ち続ける。
これはデータベースのトランザクション制御機能がトランザクションは短い時間で完了すべきものという考え方を基にしているため。
この考え方はデータベースのトランザクション制御機能を利用する場合の設計時の前提条件となる。
ただ、この考え方はバッチ処理などの大量データを長時間掛けて処理するトランザクションと非常に相性が悪い。 

第3章 接続型データアクセスと短時間トランザクション

第2部 自動トランザクション処理

第4章 自動トランザクション処理の概要

第5章 自動トランザクション処理の内部動作

第6章 サービスコンポーネントの実装方法と配置方法

第7章 サービスコンポーネントを用いたアプリケーション設計時の注意点

第3部 複雑なトランザクション制御

第8章 対話型トランザクション処理の設計方法

第9章 複雑なトランザクション制御


*1 SQLServerに限定した話をまとめるつもりがないので勝手に括弧にした・・・
*2 特定のシーンではどちらのほうが適している等々というのはありそう。
*3 DBMSのための内向けのロックという意味合い濃い?

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-08-03 (月) 20:44:16 (2758d)