web-dev-qa-db-ja.com

PostgreSQLロック待機の期間の制御

depositsというテーブルがあります

デポジットが行われると、テーブルがロックされるため、クエリは次のようになります。

_SELECT * FROM deposits WHERE id=123 FOR UPDATE_

_FOR UPDATE_がテーブルをロックしているため、別のスレッドがデータに踏み込むことなくテーブルを操作できます。

ただし、他のデポジットがテーブルのロックを取得しようとすると、問題が発生します。何が起こるかとは、テーブルのロックとpsql_commit()の呼び出しの間のどこかが失敗し、ロックを長い間保持していることです。 )。対処が必要なことがいくつかあります。

  1. ロックを取得しようとする後続のクエリは失敗するはずです。NOWAITを使用してこれを達成しようとしましたが、タイムアウトメソッドを使用した方がよいでしょう(待機するのはOKで、「愚かな時間」を待たないため)。

  2. 理想的には、パスでこれを回避し、最初のクエリでロックを一定時間だけ保持するようにします。これはpostgresqlで可能ですか?

  3. クエリに追加できる他のいくつかの魔法の関数(NOWAITと同様)は、失敗する前に4秒間ロックを待つだけですか?

  4. コードベースの痛みを伴うモノリシックなスパゲッティコードの性質により、単にグローバル構成を変更するだけの問題ではないため、クエリごとのソリューションである必要があります

あなたの助けてくれてありがとう、私はぶらぶらと進み続けますが、私はあまり運がありませんでした。私はこれを見つけたので、これはpsqlの存在しない関数ですか? http://www.postgresql.org/message-id/[email protected]

26
FaddishWorm

FOR UPDATEはテーブルをロックしているので、別のスレッドがデータを踏むことなくテーブルを操作できると思います。

いいえ。 FOR UPDATEこれらの行のみをロックするため、それらをロックしようとする別のトランザクション(FOR SHAREFOR UPDATEUPDATEまたはDELETE)ブロックトランザクションがコミットまたはロールバックするまで。

挿入/更新/削除をブロックするテーブル全体のロックが必要な場合は、おそらくLOCK TABLE ... IN EXCLUSIVE MODEが必要です。

  1. ロックを取得しようとする後続のクエリは失敗するはずです。NOWAITを使用してこれを達成しようとしましたが、タイムアウトメソッドを好むでしょう(「愚かな時間」を待たずに待つだけでよいため)

    the lock_timeout setting を参照してください。これは9.3で追加され、古いバージョンでは使用できません。

    古いバージョンの粗い概算はstatement_timeoutで実現できますが、ステートメントが不必要にキャンセルされる可能性があります。 statement_timeoutが1であり、ステートメントがロックで950ms待機する場合、そのステートメントはロックを取得して続行し、タイムアウトによってすぐにキャンセルされるだけです。あなたが望むものではありません。

    lock_timeoutを設定するクエリレベルの方法はありませんが、できます

    SET LOCAL lock_timeout = '1s';

    BEGINトランザクションの後。

  2. 理想的には、パスでこれを回避し、最初のクエリでロックを一定時間だけ保持するようにします。これはpostgresqlで可能ですか?

    statementタイムアウトがありますが、ロックはtransactionレベルで保持されます。トランザクションタイムアウト機能はありません。

    単一ステートメントのトランザクションを実行している場合は、ステートメントを実行する前にstatement_timeoutを設定して、実行可能な時間を制限できます。これは、ロックを保持できる時間を制限することとはまったく同じではありません。ロックが許可される1秒の900ミリ秒待機し、実際には100ミリ秒だけロックを保持してから、タイムアウトによってキャンセルされるためです。

  3. クエリに追加できる他のいくつかの魔法の関数(NOWAITと同様)は、失敗する前に4秒間ロックを待つだけですか?

    いいえ。

    BEGIN;
    SET LOCAL lock_timeout = '4s';
    SELECT ....;
    COMMIT;
    
  4. コードベースの痛みを伴うモノリシックなスパゲッティコードの性質により、単にグローバル構成を変更するだけの問題ではないため、クエリごとのソリューションである必要があります

    SET LOCALがこれに適しています。

    クエリのテキストでこれを行う方法はなく、別のステートメントでなければなりません。

    リンクしているメーリングリストの投稿は、実装されていない架空の構文の提案であり(少なくともPostgreSQLのパブリックリリースでは)、存在しません。

このような状況では、「楽観的ロック」と呼ばれることがある「楽観的同時実行制御」を検討する必要がある場合があります。これにより、クエリの繰り返し率が増加し、さらに多くのアプリケーションロジックが必要になる代わりに、ロック動作をより詳細に制御できます。

57
Craig Ringer