ここではレコードロックの必要性について説明します。
データベースを更新するアプリケーションを通じて、複数のユーザが同一のレコードを更新してしまう可能性がある場合、何らかの機構を用いて、更新の競合を防いだり、二重更新に折り合いをつけたりする必要があります。
例えば、同一顧客に関するデータをA・B二人の営業マンが別々の端末から更新する次のようなシナリオを考えて見ましょう。
- Aが顧客Cの更新を開始
- Bが顧客Cの更新を開始
- Aが顧客Cの電話番号を更新して「更新完了」ボタンを押す
- Bが顧客Cの電話番号を更新して「更新完了」ボタンを押す
このとき、AとBが互いに自分以外の誰かがデータを更新していることに気が付かないというのは非常に危険なことです。
このような状況を防ぐためには、一般に以下のようないくつかの方法が取られます。
- 更新開始の早い者勝ち:
Aが顧客Cの更新を開始した時点で顧客Cのレコードにロックをかけ、Bによる同一レコードの更新を禁止してしまう方法です。 - 更新完了の早い者勝ち:
Aが顧客Cの更新を完了した後では、Bが更新を確定する際に、「誰かが先にデータを更新してしまっています」というエラーが発生し、BはAが更新した後のデータを読み込んでから改めて更新を行わねばならないという方法。 - 更新完了の遅いもの勝ち:
Bが顧客Cの更新を完了した時点で、Bの手許にあったデータが優先され、Aが更新したデータは無効になってしまう方法。
TOracleDataSetは、テーブルを更新するオンラインアプリケーション向けに、それぞれのポリシーにあわせて動作できさせることができるよう、LockingModeプロパティを用意しています。
- TOracleDataSet * property
LockingMode: * lmLockImmediate :
1.「更新開始の早い者勝ち」に相当します。更新開始時に、SELECT ...FOR UPDATE
NOWAITによるレコードロックが行われます。長時間に渡って多数のレコードがロックされるので、CachedUpdatesとの併用は避けた方が無難。また、Commitが行われるとロックが解放されるので、プログラム中の他の箇所で不用意にCommitを行わないよう、注意が必要です。-
lmCheckImmediate : 2.
「更新完了の早い者勝ち」に相当します。更新開始時と、更新完了直前の2回のタイミングで、レコードの読み込み後に誰かが先にレコードを更新していないかどうかのチェックが行われます。これがデフォルトの動作で、普通はこのCheckImmediateか、LockImmediateのどちらかを使うことでしょう。 -
lmLockDelayed :
上記のlmCheckImmediateから、更新開始時のチェックを省いたものです。更新の競合がめったに起こらない場合で、チェックによるオーバヘッドを減らしてレスポンスを向上させたい場合には便利かも知れません。 -
lmNone:
全くロックやチェックを行いません。これが使えるのはシングルユーザのアプリケーションに限ります。
-
lmCheckImmediate : 2.
LockingModeの違いを理解する上で一番有効なのは、TOracleDataSetのDebugプロパティをTrueにして、実際にアプリケーションを実行してみることです。(この実行例は、
→こちら← でも解説されています)
なお、上記の3. 「更新完了の遅いもの勝ち」に相当するのが、後述の
CachedUpdates(キャッシュアップデート)です。