5

あるプロセスが一意のユーザーIDから残高を選択して挿入を行おうとすると、残高が正しく更新されないと思いますが、別のプロセスがその前に残高を読み取ります。これを修正するにはどうすればよいですか?

CREATE OR REPLACE FUNCTION incBalance(INTEGER, BIGINT) RETURNS void AS $$   
DECLARE   
    balanceRecord record;
    newBalance bigint;  
BEGIN   
    FOR balanceRecord IN    
        SELECT balance FROM users WHERE userid = $1
    LOOP
        newBalance := balanceRecord.balance + $2;
        UPDATE users SET balance = newBalance WHERE userid = $1;   

    END LOOP; 
    RETURN;   
END;   
$$ LANGUAGE plpgsql;  
4

1 に答える 1

10

この特定のクエリでは、単一のSQLステートメントとして書き直すことができます。

UPDATE users SET balance = balance + $2 WHERE userid = $1;

より一般的には、トランザクションシステムに原子性とデータの一貫性を処理させる必要があります。Postgresでは、ストアドプロシージャは常にトランザクションコンテキスト内で実行されます。明示的なトランザクションブロックから呼び出さない場合は、ストアドプロシージャによって作成されます。

http://www.postgresql.org/docs/14/static/sql-set-transaction.htmlでは、デフォルトが十分に厳密でない場合に分離レベルを設定する方法について説明しています。

http://www.postgresql.org/docs/14/static/mvcc.htmlを読んで、特定のストアドプロシージャに適切なレベルを判断するのに役立ててください。セクション13.2.2および13.2.3に注意してください。これらの警告では、より高い分離レベルは、キャッチする必要のあるシリアル化例外の対象となり、トランザクションは一貫性を確保するためのメカニズムとして再試行されます。

このようなプロシージャがある場合は、プロシージャの最初のBEGINブロックの先頭にステートメントを追加して、トランザクションが十分な分離レベルで実行されていることを確認します。トランザクションでまだ作業が行われていない場合は、必要に応じて処理が行われます。呼び出し元のコンテキストが作業行ったトランザクションである場合、囲んでいるトランザクションブロックが分離レベルを十分に上げていないと、エラーが発生します。ここで指定したレベルよりもすでに高い場合は、分離レベルは下がりません。

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
于 2013-02-18T04:08:02.637 に答える