採番用テーブルによってidの採番を行う場合、「SELECT ... FOR UPDATE」を使えばよい、というのがここ1年で覚えた知識のひとつ。ただ、あまり深くは理解していませんでした。
一番の疑問点は、「FOR UPDATE」という文字列の指す意味。ふつうに訳すと「更新のための」となり、照会は問題なく行えそうな気がしてきます。仮に照会が行えるとしたら、極めて近いタイミングのアクセスがが発生した場合、重複したidができる可能性があることになってしまいます。
また、ロックの種類はおおまかに共有ロック(照会可能、更新不可)と排他ロック(照会・更新とも不可)があるようです。FOR UPDATEで実際に排他ロックが獲得できているのか、そのあたりも気になります。
というわけで、現案件のJava×DB2で調べてみることにしました。原始的な方法しか思い浮かばない自分にとっては、デバッグモードのあるEclipseは都合がよかったです。
検証
手順は以下です。
--SQL例 select VALUE from ID_GENERATOR where KEY = 'EMPLOYEE_ID' for update with rs
SELECTによる照会は、FOR UPDATEの有無で2通り検証しています。更新に関しては、ふつうにUPDATEでidの値を更新できるかどうかです。
DB2は「分離レベル」というものでロックの強度を指定できるようなので、その違いで検証しています。
分離レベル「UR」
単純な照会 | ○ |
FOR UPDATEつき照会 | ○ |
更新 | ○ |
分離レベル「CS」
※初期設定では、WITH句がない場合と同じ
単純な照会 | ○ |
FOR UPDATEつき照会 | ○ |
更新 | ○ |
分離レベル「RS」
単純な照会 | ○ |
FOR UPDATEつき照会 | × |
更新 | × |
分離レベル「RR」
単純な照会 | ○ |
FOR UPDATEつき照会 | × |
更新 | × |
FOR UPDATE OF ...
「FOR UPDATE OF (カラム名)」という指定方法もあるようです。WITH句と共存できるかどうかは試してないのですが、とりあえず以下のSQLではどちらもロックがかかりませんでした。
select VALUE from ID_GENERATOR where KEY = 'EMPLOYEE_ID' for update of VALUE select VALUE from ID_GENERATOR where KEY = 'EMPLOYEE_ID' for update of KEY
単純な照会 | ○ |
FOR UPDATEつき照会 | ○ |
更新 | ○ |
まとめ
DB2でテーブルによる採番を行う場合、以下のようにすればidの重複は起こらないといえます。
- FOR UPDATE句を指定する
- WITH句で分離レベルを「RS」か「RR」にする
※少しでも同時実行性を高めるために、「RR」よりも「RS」を指定するのがいいと思われます。
また言葉の意味についてですが、「FOR UPDATE」が修飾しているのはロックではなく、照会なのだと思います。「(後に)更新するための照会」であるがゆえに、ロックが必要、というわけですね。おそらく。
参考リンク
@IT:RDBMSアーキテクチャの深層(4) OracleとDB2、ロッキング・メカニズムはこれだけ違う http://www.atmarkit.co.jp/fdb/rensai/rdbmsarc04/rdbmsarc04_1.html DB2 Universal Database 分離レベル http://publib.boulder.ibm.com/infocenter/db2luw/v8/index.jsp?topic=/com.ibm.db2.udb.doc/admin/c0004121.htm DB2 UDB V8.1 デザイン・ガイド:ロック -基礎編- http://www-06.ibm.com/jp/domino01/mkt/dminfo.nsf/doc/000D73E3 浮浪プログラマの始末書:[DB] 排他制御 http://blog.livedoor.jp/froo/archives/50661135.html