【MySQL】排他制御(楽観・悲観ロック) - taishiii/study GitHub Wiki

排他制御

同一トランザクション内で

  • レコードAを更新 #1
  • レコードBを更新 #2

という処理をする際に、#1と#2の間にデータ整合性を阻害するような処理が入り込まないように管理することを 排他制御 という。

排他制御に関して、楽観ロック、悲観ロック、という観点から説明する。

例(アイテムの交換)

  1. ユーザー1のアイテムをAからBに変更
  2. ユーザー2のアイテムをBからAに変更

上記1と2を一貫性をもって処理したい。

処理1(ユーザー2がユーザー1にアイテムBを付与)の後にユーザー2がさらに別のユーザー3にアイテムBを付与するような処理を認めない。

楽観ロック

レコードに対して物理的なロックを取らず、他のトランザクションからの更新を許容する。その上でトランザクションの一貫性は保証する。

Version Numberパターン(Versionパターン)

対象レコードに対する他のトランザクションからの更新を認める楽観ロックにおいて、一連の更新処理の整合性を保つためにversionというカラムを用いて排他制御を行う方法

select version from user_data where user_id=1;    #version550が返ってくるとする
select version from user_data where user_id=2;    #version660が返ってくるとする

update user_data set item='B', version=version+1 where user_id=1 AND version=550; #ユーザー1のアイテムをBに
update user_data set item='A', version=version+1 where user_id=2 AND version=660; #ユーザー2のアイテムをAに

上記例ではどちらかの update が更新行数0を返した場合に rollback 、どちらの update も更新行数1を返した場合に commit する。

悲観ロック

トランザクションの対象レコードに対して物理的なロックを掛けることで他のトランザクションによる対象レコードの更新をそもそも認めない

select * from user_data where user_id=1 or user_id=2 for update; #ユーザー1,2のレコードに排他ロックをかける
update user_data set item='B' where user_id=1;                   #ユーザー1のアイテムをBに
update user_data set item='A' where user_id=2;                   #ユーザー2のアイテムをAに

トランザクションの最初に、更新するレコードに対して物理的なロックをかけることで、他のトランザクションが対象レコードを更新することができなくなる。

楽観ロックのメリット/デメリット

メリット

  • デッドロックが起き得ない

デメリット

  • テーブルにversion管理用のカラムを持つ必要がある
  • updateの更新行数によってcommitとrollbackの判断をしなければならない
  • rollbackが頻発する可能性
  • 対象レコードのバージョンを厳格に管理しなければならない(バージョン管理ができない更新には使えない)

悲観ロックのメリット/デメリット

メリット

  • 制御の判断が楽(ロックがかかっているレコードに対してそもそも他の更新がかからない)
  • 複数のトランザクションを順番に処理できる

デメリット

  • デッドロックが起きうる
  • トランザクション間のロック待ちが発生する