LOCKING ROWは一般に、単一の行にロックを設定するのではなく、特定のハッシュ値を持つすべての行にロックを設定します。行ロックではなく、行ハッシュ ロックまたは行キー ロックです。
LOCKING ROW修飾子を使用して、複数行ハッシュをロックすることはできません。複数の行ハッシュを伴うLOCKING ROW FOR ACCESSを指定する場合、ロックはLOCKING TABLE FOR ACCESSに変換されます。
行パーティション テーブルでLOCKING ROWを使用すると、行ハッシュ ロックまたは行キー ロックが設定されることがあります。行ハッシュ ロックは、すべてのパーティションの単一の行ハッシュにロックを設定します。行キー ロックは、単一のパーティションの単一の行ハッシュにロックを設定します。
LOCKING ROWを使用すると、プライマリ インデックスのSELECT文にUPDATE、DELETEまたはINSERTが続く2つの文が同じ行に対して同時に実行されたときに発生する可能性のある、デッドロックを防ぐことができます。
例:
ユーザーA:
BEGIN TRANSACTION; SELECT y FROM t WHERE x=1; UPDATE t SET y=0 WHERE x=1; END TRANSACTION;
ユーザーB:
BEGIN TRANSACTION; SELECT z FROM t WHERE x=1; UPDATE t SET z=0 WHERE x=1; END TRANSACTION;
UPI、NUPI、またはUSIを使用して単純なSELECTリクエストが行なわれた場合には、ロック マネージャによって行ハッシュ レベルのREADロックが割り当てられます。
ユーザーAのUPDATEリクエストのROW FOR WRITEロック リクエストは、ユーザーBのROW FOR READ ロックが解放されるまで待たなければなりません。ユーザーBのUPDATEリクエストのROW FOR WRITEリクエストも、ユーザーAのROW FOR READの解放を待っているため、ユーザーBのROW FOR READは解放されません。以前は、このようなデッドロックはLOCKING TABLE修飾子を使用して避けていました。
BEGIN TRANSACTION; LOCKING TABLE t FOR WRITE SELECT z FROM t WHERE x = 1; UPDATE ... END TRANSACTION;
すべてのAMPにわたるテーブル全体のロックは、望ましくありません。LOCKING ROWを使用することにより、すべてのAMPにわたってテーブル全体をロックする必要はなくなりました。
次に、LOCKING ROWを使用した例を示します。
ユーザーA:
BEGIN TRANSACTION; LOCKING ROW FOR WRITE SELECT y FROM t WHERE x=1; UPDATE t SET y=0 WHERE x=1 END TRANSACTION;
ユーザーB:
BEGIN TRANSACTION; LOCKING ROW FOR WRITE SELECT z FROM t WHERE x=1; UPDATE t SET z=0 WHERE x=1;
ユーザーBのLOCKING ROW FOR WRITEリクエストがユーザーAのLOCKING ROW FOR WRITEによってブロックされるため、デッドロックは発生しません。ユーザーBのLOCKING ROW FOR WRITEリクエストは、ユーザーAのEND TRANSACTION文が完了した時点で完了します。
- プライマリ インデックスまたは固有セカンダリ インデックスを使用した単一のテーブルの検索。
- SELECTリクエストに指定された場合(LOCKING ROW修飾子が、ロックされる行のハッシュをSELECTリクエストから得るため)。
- 次のいずれかのロックを格上げする場合。
アップグレードのロック | アップグレードされたロック |
---|---|
|
WRITE |
|
EXCLUSIVE |
WRITE | EXCLUSIVE |
- 基本テーブルがすでにロックされている場合。
- 集約 / DISTINCT / GROUP BYの操作がSELECTリクエストに含まれている場合。
- 以前設定されていたロックをREADからACCESSに格下げする場合。
すでにロックされている行ハッシュがない場合には、デフォルトのREAD行ハッシュ ロックではなく、ACCESSロックを設定することができます。
2つのプライマリ インデックスのSELECT文の後にプライマリ インデックスのUPDATE文が指定されており、SELECT文が異なる行ハッシュに対するものである場合、デッドロックが発生します。これは、プライマリ インデックスの更新には宛先基本テーブルに対するテーブル レベルのWRITEロックが必要なためです。
例:
ユーザーA:
BEGIN TRANSACTION; LOCKING ROW WRITE SELECT x FROM t WHERE x = 1; (x is UPI, NUPI, or USI) UPDATE t SET x=2 WHERE x=1;
ユーザーB:
BEGIN TRANSACTION; LOCKING ROW WRITE SELECT x FROM t WHERE x=2; UPDATE t SET x=1 WHERE x=2;
行ハッシュのアクセスが異なるため、ユーザーBのSELECT ROW WRITEロックが、ユーザーAのトランザクションの後ろの待ち行列に入ることはありません。ユーザーAのテーブル レベルのロック リクエストがユーザーBの行ハッシュ ロックを待ち、同時にユーザーBのテーブル レベルのロック リクエストがユーザーAの行ハッシュ ロックを待つため、デッドロックが発生します。