デッドロック対策の検討
トランザクションの範囲を小さくする。
システム全体でリソースのアクセス順を同じにする。
A/B/CのリソースがあってP1とP2のプロセスが
P1 A→C
P2 B→C
のようにアクセスすればデッドロックは起きなくなる。
リソースのことを「表」だけだと思うのは間違いである。
表のほかに、インデックス、外部キー、行もリソースの1つである。
外部キー(Foreign Key)
表Aに表Bへの外部キーが定義してある場合
表Aへの行のinsertは表Bのロックも発生する。
低レベルロック
行ロック(Row Level Lock/RLL)とページレベルロックの2種類ある。
DB2ではページレベルロックはないみたい。
行ロック
リソースのアクセス順の話しで、ほとんどの人は表のアクセス順で思考停止している。
それぞれの行もリソースの1つになる。
1つの表を2つのトランザクションが複数の行を別の順に更新するとデッドロックが起きる。
リソースのアクセス順の管理
Resource Order Manager(ROM)なるクラスをつくり
アプリがアクセスするリソースを登録する。
ROMは順番をチェックして、順番に沿ってないときは例外を発生する。
コンテナ
ホストのCICS+DB2の組み合わせではCICSがリソースのアクセス順を制御したりするらしい。
J2EEのコンテナ(CMP/CMT)は、リソースのアクセス順の管理は何もやらない。
SQLException
デッドロックのSQLExceptionをcatchして、少しThread.sleep()してリトライする。
Ethernetのパケットの衝突時のロジックを真似た実装。
フラグで調整する。
2つのロジックがリソースを逆にアクセスしていて、両方どうにもならないときは
2つのロジックの間で共有できるフラグを作成する。
フラグを取得したロジックが処理を実行できる。
クラスター環境でフラグは、
DB2のレコードにフラグを持つ。
JNDIにフラグを持つ。WebSphere5からクラスターワイドなJNDI機能が使える。
の2箇所くらいしかない。
リソースをロックする時間を短くする
ロックするリソースの数を減らす
リソースをロックする順番を統一する
ユーザがリトライする
デッドロックの発生時に「ただいま大変込み合っています。しばらくまって再操作してください。」
のメッセージを表示する。
EJBの失敗「transient」を見つけてリトライする。
Commandパターン/Facadeパターン/MDBによる実装
クラスタ全体でQueueingされる仕掛けを作る
ブラウザ→Servlet→JMS→MDB→ビジネスロジック
ブラウザ←Servlet←JMS←ビジネスロジック
システム全体でXA連携したJTAトランザクションを使う必用はない。
Automaticコミットにしてしまう。
オプテイミスティックロックのロジックにする。
メッセージ駆動型ビーン(MDB)の実装を利用する
サーブレットのリクエストをseriazise可能なメッセージオブジェクト(Commandパターン)にしておき
デッドロック発生時にJMSにキューしてしまう。
MDBは処理が成功してコミットしなければメッセージがキューに戻されるので
この実装を再起動制御に利用する。
再起動
単純に処理の再起動と言ってもデータベースの更新以外は
ロジックでリセットしなければならない。
C++のようなデストラクタがないので、次のGCまで残ってしまうリソースもあるかもしてない。
更新の遅延
すべてのupdate/insertをコミットの直前まで遅延する。
コミットの直前にまとめてバッチ的に更新する。
短時間のロックの実現
リソースの競合の回避
パフォーマンスの劇的な向上
デッドロックの最小化
これがIBMのWebSphere Commerceで見たDAOのやり方
オプティミスティックロックが必要
インデックスの再作成
DB内部のインデックスのメンテナンスの競合
インデックスの更新
インデックスページの分割
が原因でデッドロックが起きる。
インデックスのdrop/createが定期的に必用。
DBのアクセス方式を型にはめる
WebSphere CommereではアプリにCMPとIBMのデータビーンを使わせていた。
トランザクション分割
A→B→Cのような処理で、アプリケーションを分析して
AとB→Cに分けて非同期に実行する。(Jobキュー/fork)
単発のupdate文
行とページ、
行とインデックスの更新が発生してデッドロックが発生しうる。(SQLServer6.5)
DB2のロックするリソース
バッファプール
表
表ブロック
行
select文
select文の検索条件でインデックスが使えときだけ行ロック(IS)
そうでなけれ表ロック(S)がかかってしまう。
負荷テストの結果から
1人の仮想ユーザでシナリオを実行した場合のDBサーバの負荷が重いことを懸念する。
DBサーバに負荷をかけるような実装は、APサーバの台数を増やしてスケーラビリティに
対応する方針を不可能にする。
応答時間のばらつきがDBの排他制御の問題でないことを望む。
DB2はselect時にもロックをかける実装なので、アプリは気軽にDBにアクセスすべきではない。
DB2のロック制御は複雑なのでデッドロックが発生し、ロックをかけすぎるとシステム全体が遅くなってしまう。
このジレンマを解決するのに、IBMにはWebSphere Commerceのアクセスビーンのような更新をBeanで遅延する実装がある。
selectしたレコードの更新をすぐにDBに反映せずにすべてのupdate/insertをコミットの直前まで遅延する。
レコードのロックを行わないのでオプティミスティックロックを使う必用がある。
コミットの直前にまとめてバッチ的に更新することで、
短時間のロックの実現、リソースの競合の回避、パフォーマンスの「劇的な」向上、デッドロックの最小化
を期待できる。
この修正はアプリ全体の実装と基盤システム(フレームワーク)全体の修正を伴う。
End of FILE.
2003/07/18
ugya@lycos.com