5

のドキュメントからfind_or_create:

注: find_or_create() はデータベースから読み取り、結果に基づいて挿入する可能性があるため、このメソッドは競合状態の対象になります。検索が完了した後、作成が開始される前に、別のプロセスがテーブルにレコードを作成する可能性があります。この問題を回避するには、トランザクション内で find_or_create() を使用します。

find_or_create()PostgreSQL のトランザクション内で使用するだけで十分ですか?

4

2 に答える 2

6

いいえ、ドキュメントは正しくありません。トランザクションを単独で使用しても、この問題は回避できません。例外が発生した場合にトランザクション全体がロールバックされることを保証するだけであり、データベースに一貫性のない状態が保持されることはありません。

この問題を回避するには、トランザクション内でテーブルをロックする必要があります。これは、すべてのロックがトランザクションの終了時に解放されるためです。何かのようなもの:

BEGIN;
LOCK TABLE mytbl IN SHARE MODE;

-- do your find_or_create here

COMMIT;

しかし、それはすべての魔法の治療法ではありません. これはパフォーマンスの問題になる可能性があり、デッドロック(他のトランザクションが既にロックしているリソースを相互にロックしようとする並行トランザクション) が発生する可能性があります。PostgreSQL はそのような状態を検出し、競合するトランザクションの 1 つを除くすべてをキャンセルします。失敗した場合に操作を再試行する準備をしておく必要があります。

ロックに関する PostgreSQL のマニュアル。

並行性があまりない場合は、問題を無視することもできます。タイムスロットは非常に小さいため、実際に発生することはほとんどありません。害を及ぼさない重複キー違反エラーをキャッチした場合は、これもカバーしています。

于 2012-04-22T00:05:53.297 に答える
0

この実装はfind_or_create、OP で説明されている競合状態を防ぐ必要があります。

eval {
    $row = $self->model->create( { ... } );
}
if($@ && $@ =~ /duplicate/i) {
   $row = $self->model->find( { ... } );
} 

またfind_or_create()、最良のケースでは、単一のクエリに削減されます。

于 2012-04-22T12:09:56.053 に答える