同じSELECT文同士でのデッドロックが発生する原因とは?

このQ&Aのポイント
  • ORACLE10Gを使用したアプリ開発中、同じ条件のSELECT文がパラレルで実行されるとデッドロックが発生してしまう問題に遭遇しました。
  • 悲観的ロック(for update)しながら複数レコードを取得し、項目を更新するSQLを発行している中で、パラレル実行によるデッドロックを検出しました。
  • 同じ条件による悲観的ロックであれば多重実行でも処理待ちをするだけだと考えていましたが、デッドロックが発生する原因について知りたいです。
回答を見る
  • ベストアンサー

同じSELECT文同士でのデッドロックが発生

ORACLE10Gを使用したアプリ開発中です。 アプリ中、1テーブルに対して全く同じ条件で 複数レコードを悲観的ロック(for update)しながら取得し、 項目を更新するSQLを発行します。 単発では問題なかったのですが、処理時間の関係で パラレルで実行された場合にデッドロックを検出することがあります。 ORACLEのudumpの中を確認すると全く同じSELECTのSQL同士で デッドロックが発生したと表示されています。 こういった場合、どういう原因が考えられるでしょうか? (同じ条件による悲観的ロックであれば多重実行でも  処理待ちをするだけだと思っているのですが・・・) よろしくお願いします。

  • Tgoo
  • お礼率66% (6/9)
  • Oracle
  • 回答数2
  • ありがとう数5

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.2

>2つの処理がロックしてしまう、というように聞こえますが >こういうことはあるんでしょうか? あるんです。Aのプロセスも、Bのプロセスも「誰もロックしてない」って思ってますから。 Aがロックを掛けた直後に、Bは「さっき調べた時は誰もロックしてなかったから、ロックしよう」と「上書きロック」をしてしまいます。 通常は1と3の間に2が割り込まず、2が後回しになる為、以下のようにAもBも正常終了します。 1.Aがレコードのロック状態を検査。ロック無しと判定 3.Aがレコードをロック 2.Bがレコードのロック状態を検査。ロック有りと判定 4.Bはレコードのロックが解除されるまで待ち合わせ (以下略) デットロックの直接の原因は「間に割り込みが入らず一連の動作として処理しなければならない、ロック状態の検査とロック設定の間に、別プロセスが割り込むため」です。 こういうケースは、データベースがサーバー上にあり、アクセスがネットを介して行われる場合で、2つのプロセスが同時に同じ処理を行った際に多発します。 回避には、独自にセマフォ変数やミューテックス変数を用意するなどして「データベースのロック機能以外を使って」排他制御が必要です。

Tgoo
質問者

お礼

ご回答ありがとうございます。 こういうことはDBMS上ありえないと勝手に思ってました。 おかげで対処できそうです。 色々ありがとうございました。

その他の回答 (1)

  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.1

下記タイミングで処理されるとデットロックします。 1.Aがレコードのロック状態を検査。ロック無しと判定 2.Bがレコードのロック状態を検査。ロック無しと判定 3.Aがレコードをロック 4.Bがレコードを再ロック 5.Aが更新の為にレコード取得を試みる。が、4でBにロックされている為、ロックが解除されるまで処理待ち 6.Bが更新の為にレコード取得を試みる。が、3でAにロックされている為、ロックが解除されるまで処理待ち 7.めでたくデットロック 実際の処理では、レコードのロック以外に、テーブルのロック、システムテーブルのレコードロックとかも行うので、レコードロックとテーブルロックなどがかち合って、もっと楽しい事(※)が起きます(笑) (※)サーバーをリブートしないと復活しないとか、サーバーをリブートしても復活しないとか、色々。起きると死にそうな目に遭う。

Tgoo
質問者

お礼

ご回答ありがとうございます。 ちょっとだけ追加でご質問させてください。 >1.Aがレコードのロック状態を検査。ロック無しと判定 >2.Bがレコードのロック状態を検査。ロック無しと判定 >3.Aがレコードをロック >4.Bがレコードを再ロック というところですが、同時実行だと同一のレコードに対して 2つの処理がロックしてしまう、というように聞こえますが こういうことはあるんでしょうか? 私の記述がわかりにくかったかもしれませんが、 今回の対象は2つの処理で全く同じレコード郡に対しての 同時実行であった為です。 もし同時実行で後続の処理が同じレコードを 再ロックするなんてことがあるのならちょっと 考えないといけなそうなので^^;

関連するQ&A

  • Select文とデッドロックについて

    現在のような処理をしています。 SQL Server2008 R2 Expless プログラム1  select count(*) from TABLE_A プログラム2 BeginTrance SERIALIZABLE  select count(*) from TABLE_A Update TABLE_B set F1=123 Where P1=1 Commit ※ここで P1はTABLE_Bのプライマリキーとします。 この2つのプログラムを同時に実行している時に、プログラム1のSELECT文でデッドロックが発生した との連絡ありました。 プログラム2でもエラーが発生しているのかもしれませんが、現状調査不可能為不明です。 いろいろデッドロック、ロックヒントなどいろいろ調べてみた結果、なんとなく発生するかも?とは思われますが、このような場合、本当にデッドロックが発生するのでしょうか? また、発生する場合には、何と何がデッドロックになっているのでしょうか? (発生するはずがないとなると調査する視点を変えてみます。) よろしくお願いします。

  • デッドロックが発生します。

    sqlserverについて、アプリを実行すると「40001(1205)」というエラーが発生します。デッドロックが発生しているようです。原因が分かりません。どのような場合に発生するのでしょうか。?

  • デッドロックについて

    (Java + ベンダー製DB) 以下のような複数トランザクションを順番に実行した場合、 デッドロックが発生する可能性はあるのでしょうか? トランザクションA (隔離レベル:READ UNCOMMITTED) SELECT * FROM A ... トランザクションB (隔離レベル:READ UNCOMMITTED) SELECT * FROM A, B ... トランザクションC (隔離レベル:READ UNCOMMITTED) SELECT * FROM C, D ... 個々のトランザクションでは単なるSELECT文のみで、しかも 隔離レベルがREAD UNCOMMITTEDであれば デッドロックは起こりえないと思うのですが、 100多重ほどでテストが行われたところ デッドロックが発生しているとの事でした。 デッドロックが発生する原因として何が考えられるでしょうか? 個人的には、どこかで更新プログラムが実行されていたとしか 考えられないのですが・・ よろしくお願いします。

  • もうちょっと賢いSELECT文が書けないものでしょうか

    初めまして、最近SQLをかじり始めたぺーぺーです。 効率の悪いSELECT文しか書けずに困っています。 下記のテーブルsoftware_tableから、 ・name列「oracle」 ・version値が最大 のレコードに含まれるid(=3)を拾ってきたいのですが、自分の頭では副問い合わせを使う方法か、ソートを使う方法しか思いつきません。 問題は副問い合わせ・ソートを使うと計算コストが大きくなってしまうことで、できることなら副問い合わせ・ソートを使わずに解決したいのですが、何か方法はないものでしょうか? よろしくお願い致します。 ----------------------------- software_table id name version 1 oracle 1 2 oracle 2 3 oracle 3 4 postgres 1 5 postgres 2 6 postgres 3 7 postgres 4 ----------------------------- ■副問い合わせを使った方法 SELECT id FROM software_table WHERE name = 'oracle' AND version = (SELECT max(version) FROM software_table WHERE name = 'oracle'); ■ソートを使った例 SELECT id FROM software_table WHERE name = 'oracle' ORDER BY version DESC LIMIT 1 OFFSET 0; -----------------------------

  • 一括テーブルロック デッドロック対策

    はじめまして。 ken7761227と申します。 SQL Server2008R2でデッドロックが発生しまして、本来であれば、テーブルの 参照、更新順を正しくする修正をすべきなのですが、今からその修正を行うのは 困難なために、更新する側が、使うテーブルをまずTABLOCKXでロックしておく という対策をとることになりました。 ここで、A,B二つのテーブルがあったとし、 SELECT XXX FROM dbo.TableA WITH( TABLOCKX ) UNION SELECT YYY FROM dbo.TableB WITH( TABLOCKX ) というSQLでロックしようとしています。 UNIONを用いたのは、2つのセレクトの間に違うプロセスからの割り込を 防止したかったためです。 (このSQLの実行時にトランザクションをかけて、更新後のコミットまで、 排他ロックをかけようとしています。) TableA,TableBを更新しようとしているプロセス1が、上記SQLを実行しオーナーになります。 プロセス2がTableBをSELECTしようとすると、排他ロックで待たされるはずですよね? 実際ここは待たされていました。 また、プロセス2も更新を行おうとし、上記のSQLを実行すれば、プロセス1のコミット までは、処理が待たされるはずだと思っていたのですが、ここでデッドロックしてしまいます。 原因が全く分からずに困っております。 最終的に、やりたいことは、テーブルの参照、更新の順番を変えずに、デッドロックを 回避することです。テーブルロックをかけかたが、間違っていますでしょうか? また、それ以外に何か良い回避方法がありますでしょうか? どなたか、有識者の方々のご助言頂けないでしょうか。 よろしくお願いいたします。

  • デッドロック

    sqlserver2000に対してクライアントツール用に開発したAccess2000のadpファイルからデータの更新を行うと、以下のようなエラーが発生するのですが、どのような場合以下のエラーが発生するのかご意見を頂きたいと思います。 "トランザクション(プロセスID89)が、lockリソースでほかのプロセスとデッドロックしました。トランザクションがデッドロックの対象として選択されています。トランザクションを再実行してください。"

  • SELECT文でタイムアウトが起こります。

    いつも勉強させて頂いております。どうぞよろしくお願いします。 以下の環境で開発を行っています。 SQLServer2005 ASP.NET C# .NETのソースで 1つのクラスに2つのクエリ(2つともSELECT←複数テーブルを結合している)を呼び出す処理を書いています。(トランザクション処理は付けていません)2クエリとも検索対象テーブルは60万件程度のデータを保持しています。検索条件によって処理速度は違うのですが、1ユーザが最も遅い検索条件で検索をしてもタイムアウトにはなりません。しかし、2ユーザが同じタイミングで検索(どのような条件でも)処理を行うと、タイムアウトを起こしてしまいます。まれにデッドロックも起こります。 SQLServerではSelectのたびにlockがかかるのでSQL文中に「WITH(NOLOCK)」を記述するように書かれているサイトを見つけたのですが、他ユーザからの更新もあり得るテーブルなので、他の方法を探しております。 トランザクション分離レベルは規定値です。 是非、どなたかご教授ください。よろしくお願いします。

  • SQLServer2005 デッドロックの解決方法

    デッドロックから抜け出せません。 原因が分からず困っています。 何かアドバイスをいただけないでしょうか? SQLServer2005でサービスパックは何も当ててません。 レコードが15万件ほどのテーブル1があります。 主キーは列A,B,C。 インデックスは主キーに対応したクラスタインデックスのみ。 他に列C,D,Eでも一意で、これを条件に更新を行います。 以下のUPDATE文が10個ほどの並列プロセスで流れている のですが、数百回に一度デッドロックが発生します。 条件のDとEの値は最低どちらかは異なり、確かに一意の レコードに対して更新をかけています。 UPDATE テーブル1 SET F = '1' WHERE C+D+E IN (SELECT TOP1 C+D+E FROM テーブル1 WHERE D = '値' AND E = '値' ORDER BY C) SQL Server Profilerでトレースしたところ、上記のUPDATE文 のみによって引き起こされているようです。 DeadLock Graphを見るとKEYではなくPAGEに対して更新ロック(U) を取り合う様が見れるのですが、ひどくぐちゃぐちゃです。 試しに条件文のSELECTのみWITH (ROWLOCK,UPDLOCK)でデバッグ してみたのですが、大量のPAGEロック(IU)と一つのKEYロック(U)が取得されます。 また、D,E,Cの順に一意の非クラスタ化インデックスを貼ると、すっきりするのですが PAGE(IU)とKEY(U)に2つずつロックされます。(どうして一つにならない?) そもそもUPDATE条件文の中のSELECT文は何ロックを取得するのでしょうか? 更新ロック(U)、それとも共有ロック(S)を取得してからの排他ロック(X)? よろしくお願いします。

  • UPDATE文で発生するデッドロックについて、教えてください。

    はじめまして。 SQL SERVER初心者の為、ご指導の程お願いいたします。 UPDATE文で発生するデッドロックについて教えてください。 現在、ストアドプロシージャ内で、 以下のUPDATE文を使用しています。 UPDATE テーブルA SET 更新済フラグ = '1' WHERE ロック時間 =パラメータ.ロック時間 AND ロックユーザー = パラメータ.ロックユーザー AND ID = パラメータ.ID ※Where句に使用する、ロック時間・ロックユーザー・IDは、  キー項目では無く、インデックスも使用しておりません。  ただし、対象データを一意に識別できる項目ではあります。   このストアドを含む処理を、ほぼ同時実行すると このUPDATE文で「LOCK TIMEOUT」が発生し、 デッドロックとなってしまいます。 UPDATEの対象となるデータ件数は、ともに500~600件です また、対策としましてロックのヒント文を下記のように設定し、 WHERE句に設定している、ロック時間・ロックユーザー・IDに 対して、インデックスを作成してみましたが、 同様に、デッドロックが発生してしまいます。 UPDATE テーブルA (with rowlock) SET 更新済フラグ = '1' WHERE ロック時間 = パラメータ.ロック時間 AND ロックユーザー = パラメータ.ロックユーザー AND ID = パラメータ.ID SQL Serverに詳しい方、お力になっていただければ、光栄です。 よろしくお願いいたします。

  • MERGE文について

    以下のようなSQL文を作成し、Oracle10gで実行したところ、正常に実行されていたのですが、同一のSQL文をOracle9iで実行したところ、 ORA-00905: キーワードがありません。 とエラーがでてしまいました。 merge文はOracle9iから追加された?ものだったと記憶しているですが、思い違いでしょうか? それともOracle9i、Oracle10gで何か違いがあるのでしょうか? Oracleのバージョンはそれぞれ以下の通りです。 Oracle10g:10.1.0.2.0 Oracle9i:9.0.1.1.1 よろしくお願い致します。 <やりたいこと> A表のa列とb列が、B表のa列とb列と一致したものレコードのみ、B表のc列の内容にA表のc列をアップデートする。(インサートはしない) merge into 表A A using 表B B on (A.a = B.a and A.b = B.b) when matched then update set A.c = B.c /