PostgreSQLのデッドロックについて
解決済
回答 1
投稿
- 評価
- クリップ 3
- VIEW 7,755
PHP Warning: pg_q> uery(): Query failed: ERROR: デッドロックを検出しました
DETAIL: プロセス 8100 は ShareLock を トランザクション 624517 で待機していましたが、プロセス 6032 でブロックされましたこれがなぜ出てしまうのか、推測でも結構ですのでどなたか理由と解決策を教えてください。
プロセス 6032 は ShareLock を トランザクション 624508 で待機していましたが、プロセス 8100 でブロックされました
HINT: クエリーの詳細はサーバログを参照してください in pg_load_test.php on line *
エラーを再現できるスクリプトは以下です。
<?php
$dbh = pg_connect("host=localhost port=5432 dbname=postgres user=postgres");
while(1){
$res = pg_query($dbh, "UPDATE public.table SET created = NOW() WHERE field = '*****'");
if(!$res) exit();
}
条件句のfieldはINDEXが貼ってあり、複数列がHITします。
サンプルは無限ループですが、実際のバッチも数百万レコードを嘗めてUPDATEに向かいます。
このバッチは中身は一緒で並列稼働します。
最低2本の並列で事象が出ますが、実運用は7並列で稼働しています。
並列数が増えれば増えるほど、当たり前かもしれませんが、デッドロックの検出タイミングは早まります。
クライアント
Windws7
PHP5.6
サーバ
PostgreSQL9.3.5
CentOS6.5 or Windows7 (どちらでも発生を確認)
よろしくお願いします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
update文で複数レコードが更新対象となることが原因と思われます。updateの処理を段階的に見ると、
- whereで該当するレコード全てにロックを掛ける
- ロックしたレコードを全て更新
- ロック解放
対処としては、バッチの並列実行を辞めるか、ロックがぶつからないようにupdateのキーを別の物に変更する等になるかと思います。
対処について、追記します。バッチ処理を平行して実行しても対象のレコードが重複しないよう、バッチ処理別にwhere句に異なる抽出条件を追記する等です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.23%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2014/11/20 22:29
2014/11/21 09:09
ロックの順序については私も同じ認識でした…
そして発生する理由もおおよそ「自分がロック獲得したいレコードを相手がロックしている」ということなんだろうなぁ、って思っていました。
でもそれでしたら、単に自分は相手のロックが解除されるまで待てばいいのではないでしょうか?
この時点で相手にとって自分はまだロックを獲得できていないのですから、「デッド」ロックにはなりえないと思っております。
また「自分がロック獲得したいレコードを相手がロックしている」とおっしゃっている時点で相手がすでにロックしているので
「双方ロック獲得待ち」には該当しないと思います、少なくとも相手が「ロックしている=ロックを獲得している」ことと思いますので。
せっかく回答いただいたのに食いついてしまうようで申し訳ないのですが、それでも上記の状況でデッドロックは発生するのかしないのか
再度ご意見をいただけますと幸いです。
2014/11/21 09:23
2014/11/21 09:59
・処理Aはid=1,id=2の順でロックを掛けた
・処理Bはid=2,id=1の順でロックを掛けた
というシュチュエーションが有り得ます。両処理の1レコード目のロック獲得が同タイミングだった場合、2レコード目のロック獲得は双方待ち続けることにるのでデッドロックとなります。
2レコードで説明するとシビアなタイミングと感じますが、同時更新レコード件数が多かったりDB負荷が大きくて検索・更新に掛かるコストが大きい場合、コンディションが発生し易くなります。
2014/11/21 10:20
処理A:「自分がロック獲得したいレコードを処理Bがロックしている。ロックしたいレコードが全部獲得できるまで持っているロックは解放しない」
処理B:「自分がロック獲得したいレコードを処理Aがロックしている。ロックしたいレコードが全部獲得できるまで持っているロックは解放しない」
というイメージでしょうか。
2014/11/21 10:52
BlueMoonさん
ご回答ありがとうございます。
それでしたら納得です!!
ということは「該当するレコードすべてにロック」の段階では
一気にロックを獲得することができず、1行ずつロックを獲得して
最終的に対象のレコードが全ロックになるのですが、
その過程でロックが競合してしまうことになるんですね。
あとから気づいたことですが、pg_lockのlock_typeがrerationなのが少し気になりますが…
ちなみに更新コストは高いです。
純正のストリーミングレプリケーションを「完全同期型」で構築し、
pgpoolIIで分散構成になっておりますので。
スレッドの趣旨としてはもう打ち切らないとですが、もう一つ疑問があります。
この減少はPostgreSQL特有でしょうか?
MySQLでも起きうることでしょうか?
技術的には起こっても仕方がない、というより防ぎようがない気もするのですが…
PostgreSQLは社内で私しか推してなくて、だったらMySQLでいいじゃんって言われてしまうと切ないのです…
2014/11/21 11:07