web-dev-qa-db-ja.com

DB2で読み取り/更新のために行をロックするにはどうすればよいですか?

アプリケーションの競合状態を解決しようとしています。

ワークキューと多くのスレッドとして使用され、それを読み取り/更新するテーブルがあります。次のクエリは、アプリケーションから実行されます。最初に利用可能なジョブのIDを読み取り、次にそれらを予約して情報を取得します。

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL LIMIT 5; --Check available jobs
UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?); --Lock jobs
SELECT * FROM JOBS WHERE ID IN(?); --Get info about jobs to process

複数のスレッドが同時にこのテーブルにアクセスすると、問題が発生します。これは、'Thread#2'UPDATEを実行する前に'Thread#1'が行を読み取る場合に発生します。同じジョブを2回実行する。

私はクエリを次のようにラップすることで解決策を見つけました:

LOCK TABLE JOBS;
--Queries from above
COMIT;

これは正常に機能し、競合状態を防ぎますが、スレッド同士が終了するまで待機する必要があるため、少し極端です。

次のクエリのレコードにのみロックを設定できることを確認するにはどうすればよいですか?

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL LIMIT 5;

これはAS400 DB2データベース上にあり、現在はLUWです。

1
Ruslan

なぜあなたはテーブルを使用していますか? IBM iはネイティブデータキューオブジェクトを提供します...

アプリケーションがネイティブオブジェクトへの直接アクセスを提供しない言語でプラットフォーム外で実行されている場合は、常にI上にストアドプロシージャまたはユーザー定義関数を構築して、データキューへのアクセスを提供できます。

そうは言っても、あなたの質問に対する答えはどのRDBMSでも同じです。 分離レベルを使用してください。

あなたのケースでは、読み取り安定性を使用したい

レベルRRと同様に、レベルの読み取り固定(RS)により、以下が保証されます。

作業単位中に読み取られた行は、作業単位が完了するまで、異なるコミットメント定義を使用する他の活動化グループによって変更されません。 1別のコミットメント定義を使用する別の活動化グループによって変更された行(またはUPDATE行ロックで現在ロックされている行)は、コミットされるまで読み取ることができません。

ステートメントにWITH RSを追加するだけです...

SELECT ID FROM JOBS WHERE LOCKED_BY IS NULL WITH RS LIMIT 5
UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?) WITH RS
COMMIT

最初のステートメントは、(最大で)5行をロックします。COMMITが完了するまで。

テーブルがまだジャーナルされていない場合は、ジャーナルする必要があることに注意してください。

3
Charles

ロックの代わりとして(RS分離を使用して実行すると、スレッドが相互に待機し、SELECT .. FOR UPDATE)、一度に5個未満のジョブの処理を許容できる場合は、楽観的なアプローチを使用できます。

UPDATE JOBS LOCKED_BY = 'Thread#1' WHERE ID IN (?) AND LOCKED_BY IS NULL; --Lock jobs
SELECT * FROM JOBS WHERE ID IN(?) AND LOCKED_BY = 'Thread#1'; --Get info 
0
mustaccio