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〜が追加した部分。どうやらjavaでSQLServerを利用する場合には追加しなければならない重要なパラメータらしい。
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はやっぱり怖い。気になるのはOracleやPostgreSQLではこの変換デッドロックと同じ現象は起こるのだろうか。少し調べてみたところ見つからなかったけど。