7月18日(火)1、2コマ目
今日の予習
確実にDBを閉じる
データベースに接続(ConnectionのOpen()実行)すると、最後には必ず切断(ConnectionのClose())を実行する必要がある。
finally句でClose()実行
古典的な方法。
using句で自動的にClose()実行
using()の()内で宣言、生成したインスタンスはusing()につづくブロック終了時に自動的にDispose()が実行される。このDispose()内でClose()が実行される。よって、明示的にClose()を呼び出す必要がなくなる。
トランザクション処理
Transactionオブジェクトを使ってコミット、ロールバックを行う。
今日、やったこと
- DBのトランザクション(続き)
- トランザクションの実装
今日のホワイトボード
前回からの「DBのトランザクション」の続き。
トランザクションの一貫性
トランザクションのACID特性の1つである”トランザクションの一貫性”とはトランザクションを単独で実行しても、複数同時に実行しても結果は変わらない。
下図では、Aさん、Bさんの入札処理はそれぞれ単独なら問題は発生しないが、同時に実行するとタイミング次第(Bさんの書き込みがAさんより早いとき)で問題が発生する。これが一貫性が確保されていない状態。
| 図 トランザクションの一貫性が確保されていない |
トランザクションの一貫性を確保するために、複数同時に実行するトランザクションをどれくらいのレベルで分離するか4段階で決められている。
全く分離しない”Read Uncommitted”から完全に分離する"Serializable"まで4段階ある。
オラクルのデフォルトの分離レベルは"Read Committed"。別トランザクションの非コミットなデータは読み込まない(ダーティーリードは発生しない)が、コミットすれば読み込むため、ファジーリードやファントムリードが発生する。
データのロック
更新が同時に実行されないように、更新時にまず対象データをロックする。なお、このロックは自動的に行われ、コミットまたはロールバックで解除される。
| 図 更新対象行をロック |
トランザクション分離レベル"Serializable"
複数同時にトランザクションが実行されるとき、各トランザクションは完全に分離される。
よって、トランザクションAが”Serializable”でトランザクションを実行中のとき、以下のようになる。
別トランザクションが更新、コミットしたデータを読み込むと
別トランザクションの非コミットなデータはだけでなく、コミット済みデータも読み込まない
=>読み込むのはトランザクションAのトランザクション開始時のデータ
=>ファジーリードやファントムリードも発生しない
別トランザクションが更新、コミットしたデータを更新すると
トランザクションAが更新するデータを別トランザクションが更新すると、エラー発生
=>トランザクションA開始時とデータが変わっているとエラーが発生する
| 図 分離レベル"Serializable"で別トランザクションが更新した場合 |
手動ロック
更新時の自動ロックだけで対応できないケースもある。このときはロック対象行を取得するSQLに"for update"をつけて実行するとロックできる。コミット、ロールバックでロックは解消される。
| 図 手動ロック |
次回は
トランザクションの実装のつづき。