S-JIS[2008-01-27/2009-01-19] 変更履歴

排他(ロック)

RDB(Oracle)の機能の一つに、排他(ロック)がある。


SELECT 〜 FOR UPDATE

レコードをロックするには、SELECT文でFOR UPDATEを指定する。[2009-01-19]

SELECT * FROM emp WHERE empno=7902 FOR UPDATE;

FOR UPDATEでは、自分がロックしたい対象を 他のトランザクションがロックしている場合に、待つかどうかを指定するオプションがある。
WAITだと、相手がロック解除(コミットまたはロールバック)するまで無期限に待つ。
NOWAITだと、即座に戻る。(ORA-00054「リソース・ビジー、NOWAITが指定されていました。」が発生する)
省略時はWAIT(無期限に待つ)。

また、WAITにはタイムアウト時間を秒単位で指定できる。(Oracle9i以降らしい)
タイムアウトするとORA-30006「リソース・ビジー; WAITタイムアウトの期限に達しました。」が発生する。

SELECT * FROM emp WHERE empno=7902 FOR UPDATE WAIT;
SELECT * FROM emp WHERE empno=7902 FOR UPDATE NOWAIT;
SELECT * FROM emp WHERE empno=7902 FOR UPDATE WAIT 10;

JDBC(JavaからのDB接続)でタイムアウト時間をsetQueryTimeout()で指定していてそれがタイムアウトした場合、ORA-01013「ユーザーによって現行の操作の取消しが要求されました。」が発生する。


排他のタイミング

同一レコードに対して2つのトランザクションが更新系の処理を行おうとすると、先にSQLを実行した側が そのレコードに対して排他を獲得する(ロックする)。[2007-09-15]
後からSQLを実行した側は、ロックが解除(先に実行されたトランザクションがコミットまたはロールバック)されるまで停止(待機)する。(NOWAITを指定していれば待たずに終了する)

ロックされている間も、(FOR UPDATE以外の)SELECTは普通に行える。(変更前の状態が読み出せる)

上記で「更新系の処理」と書いたものは、具体的にはUPDATE・DELETE・SELECT FOR UPDATE・INSERT等。

排他がかかった場合の実行状況の例
  1.先に実行したSQL 2.後に実行したSQL 3.排他中に他トランザクションから読み込み 4A.先トランザクションがコミット 4B.先トランザクションがロールバック
UPDATE
UPDATE
update テーブル
set DATA='UP1'
where KEY=123;
update テーブル
set DATA='UP2'
where KEY=123;
ロックされる 更新前のデータが読める。 後から実行していたSQLも正常に実行され、上書きされて後のデータの状態になる。 後から実行したSQLが正常に実行される。
DELETE
DELETE
delete from テーブル
where key=123;
delete from テーブル
where key=123;
ロックされる 削除前のデータが読める。 後から実行していたSQLも正常に実行される。
(既に消えているので0件削除で正常終了)
後から実行したSQLが正常に実行される。
INSERT
INSERT
insert into テーブル
(KEY, DATA)
values(123, 'soraFC');
insert into テーブル
(KEY, DATA)
values(123, 'soraSC');
ロックされる 挿入前のデータ(すなわちデータが無い状態)が読める。 後から実行していたSQLが一意制約違反(ORA-00001)のエラーになる。 後から実行したSQLが正常に実行される。
INSERT
UPDATE
insert into テーブル
(KEY, DATA)
values(456, 'sora3rd');
update テーブル
set DATA='all';
ロックされない      
UPDATE
INSERT
update テーブル
set DATA='all';
insert into テーブル
(KEY, DATA)
values(789, 'ed7');
ロックされない      

INSERT-UPDATEの組み合わせでは、お互いにその時点でSELECTできる情報に対してしか更新しない。
INSERTデータはコミット前は自トランザクションからしか見えないので、排他の対象にならない(他トランザクションの更新対象にもならない)。

INSERT-INSERTでは、コミット前は他トランザクションからSELECTできる情報ではないが、排他はされる。


タイミングとして重要な事は、


ちなみに排他とは関係ないが、Oracleで複数のレコードにsysdateを使って日時を入れてまとめてコミットする場合、各レコードの日時はずれることがある。
PostgreSQLで同様のことをした場合は全レコードの日時に同じ値が入る。


SQLメモへ戻る / Oracle目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま