SQLServerで変換デッドロックの回避

デッドロックといえば、2つのテーブル(ここではA、B)に対して、トランザクション1はA→Bの順で更新、トランザクション2はB→Aの順で更新しようとした場合に発生する、というのは良く知られていると思う。MicrosoftのTechNetによると、このデッドロックはサイクルデッドロックというらしい。
http://technet.microsoft.com/ja-jp/library/cc707374.aspxの「3.3 デッドロックの種類 2.サイクルデッドロック」を参照。
一方で1つのテーブルに対して、複数のトランザクションで参照・更新を行う場合もデッドロックが発生する。同じくTechNetによると、これは変換デッドロックというらしい。
http://technet.microsoft.com/ja-jp/library/cc707374.aspxの「3.3 デッドロックの種類 1.変換デッドロック」を参照。

今回遭遇したのは変換デッドロックの方で、1つのテーブルに対して、2つのトランザクションから同一のキーで参照・更新を行おうとしてデッドロックが発生した。今までこのタイプのデッドロックに遭遇したことがなかったが、今回の対応で一応の回避策が分かったのでそれをまとめておく。

JDBC接続文字列

リファレンス等を読むと、SQLServerを利用する場合のJDBC接続文字列は以下のようにある。

jdbc:sqlserver://192.168.1.1:1433;databaseName=dbnamae;

ただし、このままだとデータの読み出しにカーソルが使えないので、以下のようにパラメータを追加する必要がある。

jdbc:sqlserver://192.168.1.1:1433;databaseName=dbnamae;selectMethod=cursor

selectMethod〜が追加した部分。どうやらjavaSQLServerを利用する場合には追加しなければならない重要なパラメータらしい。

SELECT 〜 FOR UPDATE

SQLServerではPostgreSQLなどで使うSELECT 〜 FOR UPDATEが利用出来ない。その代わりにロックヒントなるものを追加して、更新ロックを掛けるとSELECT 〜 FOR UPDATEと同等の処理となるらしい。

SELECT
    hoge.key
    , hoge.value
FROM
    hoge WITH(ROWLOCK, UPDLOCK)
WHERE
    hoge.key = 1;

FROM句のテーブル名後ろにあるWITH〜がロックヒント。上記例では行ロックと更新ロックを掛ける場合のSQL文となっている。ロックヒントで指定できるロックの種類は、http://technet.microsoft.com/ja-jp/library/ms187373(SQL.90).aspxを参照。

たったこれだけのことなのに、えらく時間が掛かってしまった。使い慣れていないDBはやっぱり怖い。気になるのはOraclePostgreSQLではこの変換デッドロックと同じ現象は起こるのだろうか。少し調べてみたところ見つからなかったけど。