重要なポイント

複数ユーザーが同時にデータ更新したときに不整合がおきないようにレコードをロック!

解説

通常の更新

下記の図のように、「Aさん」と「Bさん」が同時にレコード更新をした場合、どちらも在庫があると判断してしまい、更新することができてしまいます。その場合、「40」 もしくは 「60」という値に更新されてしまい、想定する処理結果ではなくなってしまいます。

排他制御(ロック)

同時に更新できないように「(レコード)ロック」をすることで、他のユーザーからの処理を受け付けなくすることができます。

  1. Aさん、ロックする
    在庫があれば〈-50〉し、在庫数「50」に更新する。
  2. Aさん、ロック解除する
  3. Bさん、ロック解除待ち
  4. Bさん、ロックする
    ロックが解除されたら、処理が始まる。
    在庫があれば〈-60〉しようとするが、在庫が足りないので更新しない
  5. Bさん、ロック解除する

ロックトラブル

レコードロックしたが、解除することを忘れてしまうと、次の処理が止まってしまいます。必ず、ロック解除するようにしましょう。

デッドロック

下記のような流れで、それぞれの処理の中で、レコードロックを解除せずに他のレコードを更新しようとすると「デッドロック」と呼ばれる状態となり、「Aさん」「Bさん」ともに処理が継続できない状態になります。レコードロックを利用する場合には、注意しましょう。

構文

レコードロックするためには、トランザクション処理開始の宣言「begin」を入れることで行うことができます。また、Select文の最後に「for update」をセットすることでレコードをロックすることが可能です。

for update(レコードロック)

-- トランザクション処理開始(※必須事項)
begin;
    select * from [テーブル名]
     where [カラム名] = [値] for update;
-- 処理を確定(※ロック解除)
commit;

<例題>

  • トランザクション処理を実行すること。
  • 処理1:「commodity_mst」を下記の条件でロックすること。
    • 商品NO(commodity_no)が〈1〉
  • コミット(commit)し、データ確定しロック解除こと。
--トランザクション処理開始
begin;

--処理1
select * from commodity_mst cm
 where cm.commodity_no = 1 for update;

--ロールバック
commit;

参考:カーソルの場合

ストアドプロシージャなどで使われる「カーソル」にも排他制御が可能です。
ここで取得した値をもとにレコード更新や削除などを行う場合には、レコードロックを行うことを推奨します。

-- 最後にfor updateを入れます。
declare datCursor cursor for
    select
     cm.commodity_no
    ,cm.stock
     from s21000000_commodity_mst cm
     where cm.commodity_no = int_commodity_no for update;

-- コミットもしくはロールバックを忘れないように
commit;