- PR -

C#でアクセス、SQLServerのデッドロック

1
投稿者投稿内容
だいご
会議室デビュー日: 2007/09/24
投稿数: 17
投稿日時: 2007-11-14 13:39
VisualStudio2003のC#(.NET1.1)で作成したPC上で動作するWindows.Formを
もつプログラム(Webプログラムではない)があります。
デザイナで SqlDataAdapter を作成し、SelectCommandだけを定義して
DataSet と SqlConnection を作成しました。
SelectCommand の SQL文は、複数のテーブルをJOINした程度の簡単な
抽出SQLで、特に難しい構文は使用してません。
接続先のDBはSQLServer2000(SP3a)で、SqlDataAdapterのFill()メソッドを
使用して SelectCommandを実行します。
このプログラムを実行すると稀になんですが、

トランザクション(プロセス ID xx)が、lockリソースでほかのプロセスと
デッドロックしました。トランザクションがデッドロックの対象として選択
されています。トランザクションを再実行してください。

という例外が発生する事があります。

確かに他の沢山のPCから、上記の同じプログラムや別のプログラムから
SELECT/INSERT/UPDATE/DELETE などの SQLを上記の対象テーブルに行いますが、
上記プログラムは、トランザクションを定義していない、ただの SELECT文
です。

なぜ、デッドロックが発生するのか、
色々調べてみたのですが判りませんでした。

同じ様な現象をご存知の方、または、原因をご存知の方は
いらっしゃいませんでしょうか?

unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-11-14 13:57
引用:

だいごさんの書き込み (2007-11-14 13:39) より:
確かに他の沢山のPCから、上記の同じプログラムや別のプログラムから
SELECT/INSERT/UPDATE/DELETE などの SQLを上記の対象テーブルに行いますが、
上記プログラムは、トランザクションを定義していない、ただの SELECT文
です。


(ちょっと良く覚えていないので、以下、あまり自信がありませんが。)

まず、自身でトランザクションを明示しなくても SELECT の内部ではなんらかのトランザクションがあるはずです。

つぎに、SELECT 文だけであっても、トランザクション分離レベル(Transaction Isolation Level)が強いとデッドロックになるのではないかと思います。たとえば SELECT だけの側のトランザクションで、トランザクション分離レベルが SERIALIZABLE などに設定されていないでしょうか?

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-11-14 22:27
パラレルワールドをワームホールでつないでおきます。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=42239&forum=7
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2007-11-14 22:43
2つ以上のロック対象リソースを同一の順番でない形でアクセスするとデッドロックが派生する可能性があります。
例を示しますとA、Bというリソースがあり、X、Yという平行に走る可能性があるプロセスがあるとします。
XはA、Bという順番でアクセスし処理が終了した段階でA、Bのロックを開放するとします。
YはB、Aという順番でアクセスし処理が終了した段階でA、Bのロックを開放するとします。
このときXがAを確保し、Bのロックを確保する前にYがBのロックを確保してしまうと両方が互いのリソースを要求し合い、ロックが永遠に開放されなくなります。
これを、デッドロックと呼びます。
なので複数のロック対象リソースがある場合は違うプロセスでも必ず同じ順番でアクセスするか、デッドロックをあらかじめ想定したプログラミングをする必要があります。
一般的なRDBMSのデフォルトではSELECT文でも共有ロックを確保するため、デッドロックは発生し得ます。

補足ですがロックが永遠に開放されないと困るのでRDBMSでは待ちグラフと呼ばれるモデルを利用し、ロック取得対象のリソースがすでにロック状態であった場合に、そのロックの保有プロセスが要求するロックが自分のリソースでないかを判断したりして、永遠のロック待ちを解消しようとします。そのためデッドロックエラーが発生するわけです。
こうした機構がないJavaや.Netなどの標準のモニタに下手にロックすると永遠のロック待ちに簡単になります。

[ メッセージ編集済み 編集者: Anthyhime 編集日時 2007-11-14 22:55 ]
だいご
会議室デビュー日: 2007/09/24
投稿数: 17
投稿日時: 2007-11-15 11:26

unibonさん
コメントありがとうございます。

> まず、自身でトランザクションを明示しなくても SELECT の内部では
> なんらかのトランザクションがあるはずです。

はい、私もそう思うのですが、トランザクションを設定していない
SqlDataAdapterのFill()メソッドの分離レベルって、どうやって
調べたら良いのか検討がつきません。
SQL Serverの方で、受け付けたSQLが、どんな分離レベルだったのか、
トレースをとる方法とかあるのでしょうか?


Anthyhimeさん
コメントありがとうございます。

> 例を示しますとA、Bというリソースがあり、X、Yという平行に走る可能性が
> あるプロセスがあるとします。
> XはA、Bという順番でアクセスし処理が終了した段階でA、Bのロックを開放するとします。
> YはB、Aという順番でアクセスし処理が終了した段階でA、Bのロックを開放するとします。
> このときXがAを確保し、Bのロックを確保する前にYがBのロックを確保してしまうと
> 両方が互いのリソースを要求し合い、ロックが永遠に開放されなくなります。

はい。
これは理解できます。でもこれは、XがBをアクセスするときと、YがAをアクセスするときのは
UPDATE等、更新系のSQLの場合ですよね?

XがAをSELECTし、BをUPDATEするトランザクション
YがBをSELECTし、AをUPDATEするトランザクション
の場合はデッドロックになりますよね?

でも

XがAをSELECTし、BをSELECTするトランザクション
YがBをSELECTし、AをSELECTするトランザクション
の場合は、共有ロックが重なってかけられるだけで、SELECTはできますよね?

それから

XがAをSELECTし、BをSELECTするトランザクション
YがBをSELECTし、AをUPDATEするトランザクション
の場合、YのAに対するUPDATEが早かった場合、XのBに対するSELECTは
分離レベルによって、ダーティリードするか、待たされるか
のどちらかじゃないのですか?
1

スキルアップ/キャリアアップ(JOB@IT)