2012-04-09 11 views
8

PostgreSQLデータベースのPL/pgSQL関数のデッドロック問題が発生しました。コードブロック(例)のSQL文を見つけてください:PL/pgSQL関数でデッドロックが検出されました

BEGIN 
UPDATE accounts SET balance = 0 WHERE acct_name like 'A%'; 
UPDATE accounts SET balance = balance + 100 WHERE acct_name like '%A'; 
EXCEPTION WHEN OTHERS THEN RAISE NOTICE SQLERRM; 
END; 

このステートメントの実行中にデッドロックが発生していることがわかりました。しかし、私は同時にこのテーブルを更新しようとしている他のステートメントがあるのか​​どうかはわかりません(ロギングシステムで何も見つからなかったためです)。

このステートメントでデッドロックが発生している可能性はありますか?私が知る限り、BEGIN/ENDで声明全体をブロックしたとします。同じトランザクションがあり、ロックされてはいけません。

+0

アカウントにトリガーがありますか?また、明示的なロックを使用していますか? – strkol

+1

デフォルトでは、トランザクションは他のトランザクションによってコミットされた変更を監視できます。詳細については、PostgreSQLのマニュアルから[Transaction Isolation](http://www.postgresql.org/docs/current/static/transaction-iso.html)を参照してください。 –

+0

@strkolはい、ありますが、そのトリガーのステートメントはこのテーブルに関係しません。明示的なロックのためにもyesです。 –

答えて

11

間違いなく他のプロセスが同じリソースに対して競合しています。それがデッドロックの性質です。表示しているような関数はデッドロックすることはありません。 PostgreSQLの並行性についての専門家であるcomment by @kgrittn belowを参照してください。

ご使用のバージョンのPostgreSQLが見つかりません。現代版ではという詳細なエラーメッセージが発生します。リソースを競合する両方のプロセスは、標準ログ設定で詳細にリストされています。 dbログを確認してください。

エラーを捕まえたという事実は、Postgresが完全な詳細を与えるのを妨げる可能性があります。 dbログに情報がない場合は、EXCEPTIONブロックをplpgsql関数から削除し、再試行してください。

デッドロックを軽減するには、いくつかのことができます。すべてのクライアントが同期された順序でリソースにアクセスすると、デッドロックは発生しません。このマニュアルでは、ほとんどのケースを解決するための基本的な方法について、deadlocksの章で説明しています。より新しいバージョンへのアップグレードを検討:バージョン8.3については


。特に、バージョン8.4で、この改善は、(quoting the release notes)あなたのために興味深いものになるはずです。

デッドロックを報告する際、サーバーログ(板垣孝宏)に デッドロックに関係するすべてのクエリのテキストを報告

また、バージョン8.3はend of life in February 2013になります。アップグレードの検討を開始する必要があります。

VACUUMを含むデッドロック状況はfixed in 8.3.1であったはずです。

+0

私はpostgresを持っています8.3.6。このステートメントは、たとえば単に使用します。実際には、挿入ログをRAISE NOTICEではなく別のテーブルに使用します。 –

+0

通常、エラーメッセージには必要な情報が表示されます。私は私の答えに少し追加しました。 –

+0

おかげさよ、ありがとうございます。私は他のプロセスがこのテーブルで何かをしようとしていたものを探し続けます。 –

-1

PostgreSQLでは、beginはバッチトランザクションを開始することを意味します。

最初の更新でアカウントの行がロックされますWHERE acct_name like 'A%'; これらの行は最初の更新後に排他的にロックされます。

2回目の更新で最初の更新と全く同じ行が開こうとしますが、最初の更新がまだコミットされていないため、 を更新できません。

したがって、2番目の更新ヒットデッドロックはロールバックでした。

+0

PL/pgSQLで 'BEGIN'はコードブロックの始まりにすぎません。プレーンSQLの 'BEGIN' /' COMMIT'と同じではありません。 –

0

コミットを追加して排他ロックを解除すると、デッドロックの問題は発生しません。

BEGIN 
UPDATE accounts SET balance = 0 WHERE acct_name like 'A%'; 
COMMIT; 
UPDATE accounts SET balance = balance + 100 WHERE acct_name like '%A'; 
EXCEPTION WHEN OTHERS THEN RAISE NOTICE SQLERRM; 
END; 
+1

plpgsqlのプロシージャが単一トランザクション・コミット・ブロックで実行されるため、プロシージャ内にコミットはありません。 – sharafjaffri

関連する問題