SQL ロックの種類(共有ロック/排他ロック)

SQLのロックの種類についてです。

ロック ロックとは
  共有ロック
  排他ロック
  デッドロックとは
楽観的ロック(アプリ側)
  悲観的ロック(select-for update)

ロックとは

データを処理中に他ユーザーに更新等の処理をさせないという仕組みです。排他制御とも言います。
ロックの種類として共有ロック、排他ロックがあります。
ロックの範囲もテーブル全体にかける、特定の行にかける等種類があります。

 

共有ロック

他のトランザクション:読み込みは可、更新は不可

ユーザ1が行001を読み込んでいる間は、ユーザ2は行001を読み込みはできますが、更新はできません。
→共有ロックです。読み取りロックとも言います。

ユーザ2が行001をselectした時点でも共有ロックがかかります。
→複数ユーザが行001に対して同時にかけることができます。

 

排他ロック

他のトランザクション:読み込みと更新ともに不可

ユーザ1が行001を書き込んでいる間は、ユーザ2は行001を読み込み、書き込みともにできません。
→排他ロックです。占有ロックとも言います。

ユーザ2は行001に対して共有ロックも排他ロックもかけることはできません。待ちの状態になります。

 

デッドロックとは

2つのトランザクションがともに他方のロック解除待ちの状態

ユーザ1が行001に対してを排他ロックをかけてupdate1を行いました。commitしていません。
ユーザ2が行002に対してを排他ロックをかけてupdate2を行いました。commitしていません。

ユーザ1が行002に対してをupdate3を行おうとしますが排他ロックがかかっていて待ちの状態になります。
ユーザ2が行001に対してをupdate4を行おうとしますが排他ロックがかかっていて待ちの状態になります。

この場合データベースの機能によってはデッドロックを検知して両者の更新を失敗させることができます。

 

楽観的ロック(アプリ側)

アプリ側で制御する

データ取得時にロックを掛けず、データ更新直前にアプリ側でデータ取得時点からデータが変更されていないかチェックしてから更新するやり方です。

例:一覧画面で1行を選択して変更画面に遷移します。このときテーブルから選択したデータの更新日時を取得しておきます。
次に値を変更して変更ボタンを押したとき、取得しておいた更新日時とテーブルの更新日時を比較し値が同じか確認します。

値が同じ・・・他の人が更新していないので更新する。
値が異なる(テーブルの値が新しい)・・・他の人が更新したので更新しない。

データが変更されていないかチェックする項目として更新日時や専用のバージョン管理項目を使用します。
ロックの待ちは発生しません。

更新が少ない環境で使用します。

 

悲観的ロック(select-for update)

データベースのSQL(select-for update)で制御する

データベースの機能であるSQL(select-for update)で排他ロックを掛けてから、データを更新してロックを外します。

データが変更されていないかチェックする項目は不要です。
ロックの待ちが発生します。

更新が多い環境で使用します。

Oracle select for updateのサンプル

参考
https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%83%E3%82%AF_(%E6%83%85%E5%A0%B1%E5%B7%A5%E5%AD%A6)

関連の記事

SQL トランザクションとは

△上に戻る