DB2の「Select for update」は「With RS」か「カーソル」を使わないと有効にならない。

DB2のロックの仕組みはDB2を使っている人には当たり前なのかもしれないのですが、Oracle使いにとってはかなり異様な仕様になっています。
そんなななかで今日また新しい驚きを得たのでメモ。

なんとDB2の「Select for update」は「With RS」か「カーソル」を使わないと有効にならないということが分かりました。
Oracleの場合以下のSQLでTESTTBLのID='1'の行をロックすることが可能です。

SELECT * FROM TEST WHERE ID='1' FOR UPDATE

しかしDB2の場合上記SQLではロックできず他プロセスから更新されてしまいます。(分離レベルがデフォルトのCSの場合)更新しないようにするには以下のように「WITH」句で「RR」か「RS」をつける必要が有ります。

SELECT * FROM TEST WHERE ID='1' FOR UPDATE WITH RS

これは驚いた。
デフォルトのCS分離レベルでのSQLDB2発行しても何も言わずにそのまま通ってしまいロックが架かっていない状況になってしまうのがびっくりでした。

WITH句をつける以外にはカーソルを付ける方法が有りPositioned Update (FOR UPDATEつきのSelect実行後、getCursor()にてカーソルを取得し、WHERE CURRENT OFカーソル名という条件をつけたUpdateを実行する)を行う必要が有ります。
これはJDBCを使って実装するには不便なのであまり使われなそうなのでDB2のFOR UPDATEにはWITH句を忘れずにした方がよさそうですね。
http://www-01.ibm.com/support/docview.wss?uid=std327fede7ab1fcd7ab4925732b0007cc2c

ついでにDB2Oracleの違いのまとめ

マルチバージョン一環制御(MVCC)とロックによる一環制御

OracleにはUNDO表領域によるマルチバージョン一環制御(MVCC)がありますがDB2には存在しない(v9.7からは似たような仕組みのCurrently Committed*1が使えるようになっています)ことが大きなポイントになっています。(OracleMysqlはMVCCでDB2SQLServer*2はロック方式)
Oracleは他のトランザクションにより更新中のレコードが存在してもUNDO表領域から更新前情報を読み取り返却することが出来ます。しかしUNDO表領域を持たないDB2の場合更新前情報は既に失われている為更新トランザクションが確定されるまで検索が待機してしまいます。

これは激しくよく問題になるので一般的な知識としてよく知られていることだと思います。

リードロックの有無

MVCCがあるためOracleはリードロックを取得しません。しかしDB2はカーソルが行上にある間必ずリードロックを取得します。
まあリードロックは直ぐ開放されるのであまり気にしなくて良いでしょう。

*1:MVCCと似ていますがバージョニングでないので自分より後のトランザクションの結果を拾うこともある点が異なるようです。逆にそのぶんOracleより性能面で勝っているそうです

*2:2005からはMVCCも使える