排他制御

21.3 排他制御

3. 排他制御

  • 排他制御
  • 3つのロックモード

排他制御

排他制御とは、あるデータベーステーブルのデータを更新するときに、他のプログラム(他ユーザ)から同じデータを同時に更新できないようにする仕組みです。
同時更新が起きると、後から更新した内容で上書きされてしまうなど、データ不整合の原因になります。

ABAPでは、更新対象を守るために「論理ロック(enqueueロック)」を使うのが基本です。
論理ロックは「このデータは今だれかが使用中」という情報を、ロック管理(enqueue)に登録して共有することで排他を実現します。
各プログラムは更新前にその情報を参照し、すでにロックされていれば更新を中止します。

3つのロックモード

ロックには用途に応じて複数の「モード」があり、他のロック要求を許可するかどうかが変わります。

・X:排他ロック(Exclusive/累積なし)
・E:排他ロック(Exclusive/累積あり)
・S:共有ロック(Shared)

● X:排他ロック(Exclusive/累積なし)

Xは排他ロックで、他ユーザーのロック取得を許可しません。(Eと同じく)ただし、Xは 「累積なし」が特徴です。「累積なし」とは、同一セッションで同じデータに対してロックを二重に取得できない、という意味です。 すでにXロックがかかっている状態で、同じデータにもう一度Xロックを取得しようとすると、ロックエラーになり得ます。

● E:排他ロック(Exclusive/累積あり)

Eは、主に更新(登録・変更・削除)のためのロックです。 同じデータに対して、他ユーザーのロック取得を許可しません、(ロックモードどれでもNG)。
更新するときは「自分だけが触れる状態」にする必要があるためです。

また、Eの特徴として「累積(るいせき)あり」があります。
これは、同一セッション(同じ処理の中)で同じデータを複数回ロックしても問題なく取得でき、 ロックが回数分だけ積み上がるという意味です。

例)同じデータにEロックを2回取得すると、ロックは「2回分」かかっている状態になります。
この場合、解除(DEQUEUE)も1回では足りず、取得した回数分だけ解除して、ようやくロックが完全に外れます。
つまりEは、「同じ処理の中で、入れ子でロックしても動くように、回数で管理する」タイプのロックです。

● S:共有ロック(Shared)

Sは、主に参照(表示)のためのロックです。
同じデータに対して複数のユーザーが同時にSを取得できます。 (=S同士はOK。参照だけなら複数人で見てもよい、という考え方です)
表にすると以下の表のようになります。 ロックモードの表に出てくる OK/NG は、次の意味で統一します。
・OK:ロック取得できる(ロックエラーにならない)
・NG:ロック取得できない(ロックエラーになる)

・別セッション(別オーナー):同じ対象に E/X が絡むと衝突して取れない
・同一セッション(同じオーナー):E/Sは取れるが(再取得・切替も可)、Xが絡むと衝突扱いになり取れない

■ まとめ(覚え方)

・参照(表示)なら S:S 同士は OK(複数人で見られる)
・更新(登録・変更・削除)なら E:自分以外は入れない(排他)
・E は累積あり:同じ処理内で同じロックを複数回取ってもOK(回数分だけ解除が必要)
・X は累積なし:同じ処理内でも二重ロックできない(2回目でエラーになり得る)

教材ではまず、「参照は S、更新は E」を基本として覚えると理解しやすくなります。

ロックオブジェクトの登録方法

1. SE11からロックオブジェクト名を入力 ※「E」始まり+DBテーブル名で命名します。

2. 各項目を入力します。

内容説明 :テスト用ロックオブジェクト
名称    :E+ロック対象のテーブルIDを入力
ロックモード :E(書込ロック)を選択

3. ロックパラメータにロック対象テーブルのキー項目が設定されていることを確認し、保存、有効化します。

4. ジャンプ>ロックモジュール からロックモジュール名を確認

5. トランザクションコード:SE37からロックモジュールが自動生成されていることを確認します。 (ENQUEUE + ロックモジュール名称 / DEQUEUE + ロックモジュール名称)

※SAPの場合は、ロック処理は必ずロック用の汎用モジュールにより実施します。
ロック/アンロック用汎用モジュールはロックオブジェクトの作成により自動作成されるものとなりますので、 もし上記のようなテーブルアクセスを行う場合は、ロックオブジェクト作成が必要となりますが、 特にそのような処理が不要であれば、全てのテーブルにロックオブジェクトを作成する必要はございません。

ロック処理実装例
REPORT Z_SAMPLE213_LOCK.

START-OF-SELECTION.

  " ロック対象キー(例:品目コード)
  DATA LV_MATNR TYPE MATNR VALUE 'MA001'.

  " ロック取得(通常は 'E':更新用の排他ロック)
  CALL FUNCTION 'ENQUEUE_EZT200400101'
    EXPORTING
      MODE_ZT200400101 = 'E'
      MANDT            = SY-MANDT
      MATNR            = LV_MATNR
    EXCEPTIONS
      FOREIGN_LOCK     = 1
      SYSTEM_FAILURE   = 2
      OTHERS           = 3.

  IF SY-SUBRC <> 0.
    WRITE: / |ロック取得失敗:MATNR={ LV_MATNR } SY-SUBRC={ SY-SUBRC }|.
    RETURN.
  ENDIF.

  WRITE: / |ロック取得成功:MATNR={ LV_MATNR }|.

  " ※ここにDB更新(INSERT/UPDATE/DELETE)処理を書く

  " ロック解除(最後に必ず解除)
  CALL FUNCTION 'DEQUEUE_EZT200400101'
    EXPORTING
      MODE_ZT200400101 = 'E'
      MANDT            = SY-MANDT
      MATNR            = LV_MATNR.

  WRITE: / 'ロック解除完了'..
■ 補足(よくある質問への回答)

Q1:MODE_… と MANDT は、汎用モジュールのデフォルト値を使っていますか?
A:はい。サンプルでは、ロック用汎用モジュール(ENQUEUE)の 基本的な指定として、次の値を渡しています。

MODE(ロックモード):ロックの種類を指定します。
特に指示がなければ、更新を想定した ‘E’(排他ロック) を使用します。
MANDT(クライアント):システム変数 SY-MANDT を指定します。 これにより、ロックは「同じクライアント内」で有効になります。

Q2:MATNR = ‘MA001’ は、どのように決めていますか?(ロックFMのデフォルトは空白なのに?)
A:MATNR は「ロックをかけたい対象データ(キー)」を指定するための項目です。
サンプルでは、例として 品目コード MA001 を処理する場面を想定しているため、’MA001′ を指定しています。
ロックは「テーブル全体」にかけるのではなく、通常は 処理対象のキーを指定してロックします。
そうすると、同じテーブルでも “同じキーのデータ” に対してだけ、同時更新ができなくなります。

■ ロックの範囲の考え方

ロック用汎用モジュールは、パラメータを指定するほどロック範囲が狭くなります。

1) MANDT だけを指定する場合(キー未指定)
・ロックの範囲が広くなります。
・同じクライアント内で、そのテーブルに対する処理が衝突しやすくなります。

・イメージ:
「同じクライアントのユーザーが、そのテーブルのどの品目を処理しようとしてもロックエラーになる」 (=ロックが広すぎて実務では使いにくい)

2) MANDT + MATNR を指定する場合(キー指定)
・ロックの範囲が狭くなります。
・同じクライアントでも、同じ品目コードの処理だけが衝突します。

・イメージ:
・ユーザーAが MA001 をロック中
→ ユーザーBが MA001 を処理しようとするとロックエラー

・ユーザーBが MA002 を処理するならロックエラーにならない
→ 異なる品目コードなら同時に処理できる

ロックは「クライアント」+「処理対象キー(例:MATNR)」まで指定して、必要な範囲だけにかけるのが基本です。

上記の内容は、SE11でロックオブジェクトを作成したら、自動で生成されるのか?
→ロック/アンロック用汎用モジュールのパラメータは  ロックオブジェクト作成時に自動生成されます。
 なお対象テーブルのキーの数に応じてパラメータ数が変わります。

・自動で生成される場合、作成されるタイミングはいつ?
→ロックオブジェクトを有効化した段階で作成される。

ロックされているかどうかの確認方法

1. サンプルコードのロックモジュール呼び出しの行にブレークポイントを設定し、シングルステップで汎用モジュールを実行するところまで進みます。

2. 新規GUIセッションを開き、トランザクションコード:SM12で、対象のテーブル名等を入力し、「検索」を押下します。

3. 画面下に「品目コード:MA001」のレコードがロックされていることを確認します。

4. ソースコードでロック解除モジュールを実行し、ロックを解除します。

5. ロックが解除されたため、「検索」に何も表示されてない点を確認します。


NEXT>> 第22章 モジュール化

研修の詳細はこちら