鍵は死んだ

デンっ!

うん、日本語でおk
ロック個のリソースって何よ

それはともかく、どうやらついに出くわしてしまったようだ。デッドロックデッドロックねぇ……デッドロックって何だっけ。ポン酢醤油のある家だっけ。
アレだよね、Aテーブル→Bテーブルの順で更新する処理と、Bテーブル→Aテーブルの順で更新する処理が同時に行われると、お互いにお互いの処理が終わるのを待ち続けて動かなくなるという、聖闘士星矢で言うところの千日戦争状態になることだよね。

さて困った。デッドロックは未経験なのです。ってか、自分の携わるシステム規模から言って、まず遭遇することは無いだろうと思っていたんですけどね。なので、予備知識が殆どナシの状態からトラブルシュートスタート。ほんとこういう時ネットって便利。

……ただ、今回のシステム、別にデータ更新処理なんか2処理しかないし、そもそも複数のテーブルなんか参照しない(JOINもしない)んだけどなぁ……

======================================================
[BGM:ファイナルファンタジータクティクス戦闘準備]
【今回の環境】
DBMS:SQLServer2008ExpressR2
・アクセスクライアント数:10台前後
・各クライアント1分に1回くらいはデータの更新を行い、15秒に1回データの参照を行っている。
・データ件数:対象となる業務テーブルでは2万レコード超えない程度。

【おおまかな調査手順】
1:DBMSデッドロックのログをキャッチするよう設定
2:現象が再現するのを待つ
3:ログを確認
4:その後はその後の風が吹く


●ログの取り方●
1:Management Studioを開き、SQLウインドウで下記の二つのコマンドを打ち込む。

DBCC TRACEON(1204,-1)
DBCC TRACEON(3605,-1)
※ログ取りは1204のフラグだけでもいいのだけれど、3605もオンにしておかないと、セッション終了時にログ取りも終了してしまうらしい。

2:現象が発生したら、そのDBが存在するインスタンスのフォルダの中から、ERRORLOGファイルを取得する
(パス例:C:\Program Files\MicrosoftSQLServer\MSSQL10.50\MSSQL\Log\ERRORLOG)
※ERRORLOGは拡張子無しのファイル

3:ERRORLOGをメモ帳で開く。眺めていると、な〜んとなくデッドロックしてる処理の部分が解かるかもしれない。
============================================================

ってな感じでやってみました。で、何にも解からないけど、とりあえずログを眺めてみますかねえ……って、あ、見っけた。……ってか、結構発生しているなぁ。

ログの各々の意味はサッパリ解からんかったし、多分自分には一生理解不能な内容なんだろうなという感じだったのですが、デッドロックが発生したSQL文はすぐに解かります。結構ログの中で主張してます。さてさて、どんな処理がデッドロックになっていることやら……ってあれ……?

[SQL文その1]
UPDATE テーブルA SET データA = '1',TimeStamp = '2014/XX/XX XX:XX:XX',データB = '001' WHERE データC = '1234567890' AND データD = '1'

[SQL文その2]
SELECT COUNT(*) FROM (SELECT テーブルA.データC FROM テーブルA WHERE テーブルA.データA = '1' GROUP BY テーブルA.データC)

う〜ん……?えーと、UPDATE文とSELECT文がかち合ってデッドロックになってるの……?それに、やっぱりテーブルも1つしか使ってないし、この状況でデッドロックってどういうことだろう……

と思ったら、これと同じ状況のデッドロックに関する記事があっちこっちで見つかった。どうもこれは、SQLServer特有の「変換デッドロック」って奴らしい。ってか、デッドロックってそんな何種類あるものだったんだ。よく死ぬ鍵だなぁ。
どうも、下記のような理屈で起きるらしい。(素人が適当に理解した内容なので、真に受けないように)

1:SELECT文を発行するとき、対象範囲に「共有ロック」がかかる
2:UPDATE文を発行するとき、対象範囲に「更新ロック」がかかる
3:共有ロックと更新ロックは、別処理から同時にかけることが出来る。
4:更新ロックは、内部的にはデータ走査時は「共有ロック」、データ更新時に「排他ロック」として動作する。
5:上記ロックが同時にかけられた際、共有ロック側(つまりSELECT文側)は、最新のデータを参照したい為、更新処理を待つ
6:一方、更新ロック側(つまりUPDATE文側)からは、データ更新の為に「排他ロック」に変更するべく、「共有ロック」の開放を待つ(排他ロックは、他のロックと共存できない為)
7:あら、デッドロックの出来上がり。

理屈はなんとなく理解したし、変換デッドロックの現象と一致するので、恐らくこれなんだろうというところまでは解かったけど、さて、これどうやって解決すりゃいいってんでしょ。教えてエラくもエロくも無い人