3.4.4 デッドロックと回避策
- 〈この項の構成〉
(1) デッドロックの発生要因
二つのトランザクションが二つ以上の資源の確保をめぐって互いに相手を待つ状態となり,そこから先へ処理が進まなくなることをデッドロックといいます。
デッドロックは,一般に参照のトランザクションと更新(削除を含む)のトランザクションとの間で多く発生します。したがって,UAPのアクセス順序を変えることで,デッドロックの発生頻度を低減させることができます。
デッドロックの例として,二つのトランザクションが同一キーを持った行に対して同時に実行した場合に排他の掛かる順序とデッドロックの関係を次の図に示します。
また,ページ排他の場合には,UAPのアクセス手順を統一しても,デッドロックが回避できないことがあります。
ページ排他でのデッドロックの例を次の図に示します。
図「ページ排他でのデッドロックの例」で示した例の場合,クラスタキーを指定していないと,ページへの行の格納順序を一定にできないため,ページ単位にUAPのアクセス順序を統一できません。このような場合には,ALTER TABLEでページ排他を行排他に変更してデッドロックを回避してください。
(2) サーバ間で発生するデッドロック
デッドロックは,一つのサーバ内で発生する以外にサーバとサーバの間でも発生します。HiRDB/パラレルサーバでは,サーバ間で発生するデッドロックのことをグローバルデッドロックといいます。
グローバルデッドロックは,一般に図「ページ排他でのデッドロックの例」で示したような一つのサーバ内で発生するデッドロックと同様に,参照のトランザクションと更新のトランザクションとの間で発生します。したがって,UAPのアクセス順序を変えることで,デッドロックの発生頻度を低減させることができます。
グローバルデッドロックの例として,二つのトランザクションが別々のサーバに格納された表に対して,検索と更新の順番を逆順で実行した場合に排他の掛かる順序とデッドロックの関係を次の図に示します。
図「グローバルデッドロックの例」で示した例の場合,各バックエンドサーバでは,UAP1とUAP2との間の排他制御になりますが,フロントエンドサーバ側からは,お互いに排他待ちとなっているのでデッドロックになります。
(3) デッドロックの検出
各ユニット内でのデッドロックは,それぞれのユニット内の排他制御機構が検出します。HiRDBでは,各ユニット内で同一サーバが排他したリソースについては,そのサーバ内の複数トランザクション間のデッドロックの検出を行います。しかし,同一ユニット内であっても,別サーバにわたるリソース間でのデッドロックについては,排他制御のタイムアウトによってHiRDBが検出する以外では,検出できません。複数のユニットにわたるリソース間のデッドロックについては,ユニット内の別サーバ間のデッドロックと同じであり,タイムアウトによって検出します。
(a) デッドロックの検出方法とタイミング
排他制御処理を分散させているかどうかによって,デッドロックが発生したときの検出方法とタイミングが異なります。排他制御処理の分散については,マニュアル「HiRDB Version 9 システム運用ガイド」を参照してください。デッドロックの検出方法とタイミングを次の表に示します。
pd_lck_deadlock_checkの値 |
pd_lck_pool_partitionの値※ |
デッドロックの検出方法 |
デッドロックの検出タイミング |
---|---|---|---|
Y |
2以上 |
デッドロック監視プロセスによって,定期的に検出を実行します(インターバル監視方式)。 |
pd_lck_deadlock_check_intervalオペランドに指定した間隔で検出します。この場合,デッドロック発生から検出までに時間差が発生します。 |
1 |
サーバプロセスによって,排他待ちになった時点で自己検出します(即時検知方式)。 |
デッドロック発生後,即時に検出します。 |
|
N |
− |
デッドロックを検出しません。この場合,pd_lck_wait_timeoutオペランドに指定した時間が経過するまで排他待ちを行った後,UAPに対して排他タイムアウトとしてエラーを発行します。 |
− |
(b) デッドロック検出の無効化
pd_lck_deadlock_checkオペランドにNを指定することで,排他制御でのデッドロック検出を無効にできます。
デッドロックの検出方法がインターバル監視方式の場合は,排他制御用プールパーティション数を増加するとデッドロックの検出タイミングごとに排他制御の性能が劣化するおそれがあります。そのため,デッドロックが発生しない業務システムでは,デッドロック検出をしないことでSQLの実行性能が改善することがあります。この場合は,デッドロックが発生しない業務システムを構築した上で,デッドロックの検出を無効にすることを推奨します。
反対に,デッドロックが発生する業務システムでは,デッドロック検出を無効にしないでください。デッドロック検出を無効にすると,デッドロックが発生したときにpd_lck_wait_timeoutオペランドに指定した時間が経過してタイムアウトするまでSQLがエラー終了しません。また,HiRDBがデッドロック情報を出力しなくなるため,デッドロックが発生した要因が分からなくなるおそれがあります。
(4) デッドロックの対策
デッドロックの発生原因は,大きく分けると次の二つになります。
-
UAPのアクセス順序(排他を掛ける順序)に依存する。
-
検索と更新との間の逆順処理をする。
デッドロックは,次の図で示した以外の種類もあります。
-
図「デッドロックの例」
デッドロックの主な種類とその対策を次の表に示します。
(5) デッドロックプライオリティ値による排他制御
デッドロックが発生した場合,どちらのトランザクションをエラーとするかをデッドロックプライオリティ値で制御できます。この制御は,システム定義のpd_deadlock_priority_useオペランドで,デッドロック優先順位を制御する指定をし,クライアント環境定義のPDDLKPRIOオペランドでデッドロックプライオリティ値を指定した場合に,その指定値によってHiRDBがデッドロック優先順位を決定します。
指定した値が小さい方が処理の優先度が高く,値が大きくなるほどエラーになってロールバックする可能性が高くなります。また,デッドロックプライオリティ値が同じ場合,後発の方がエラーとなってロールバックさせられます。
PDDLKPRIOオペランドの指定を省略した場合,UAP,ユティリティ,及び運用コマンドの種別によって,自動的にHiRDBがデッドロックとなったトランザクションのどれかをエラーにしてロールバックさせます。PDDLKPRIOオペランドの指定値を省略した場合に仮定される値については,「クライアント環境定義の設定内容」を参照してください。
デッドロックによって暗黙的にロールバックされたUAPは,ROLLBACK文,又はDISCONNECT文でトランザクションを終了させないと,SQL文を実行してもエラーになります。また,OLTP環境でX/Openに従ったアプリケーションプログラムをクライアントとした場合に,実行したアプリケーションプログラムがデッドロックになったときもトランザクションの終了が必要です。
なお,デッドロック発生時にデッドロック情報を出力したい場合,システム定義のpd_lck_deadlock_infoオペランドにYを指定する必要があります。pd_lck_deadlock_infoオペランドについては,マニュアル「HiRDB Version 9 システム定義」を参照してください。
(6) デッドロックの回避策
デッドロックは排他の範囲を広くすることで発生頻度を低減できますが,同時実行性は低下します。したがって,排他の範囲を狭くすると同時実行性は向上しますが,不正参照,及び不正更新を引き起こしたり,デッドロックの発生率が増加します。同時実行性を保ちながら,デッドロックを回避するため,次に示す対策を考慮する必要があります。
-
頻繁に更新する列にはインデクスを付けないようにする。
-
検索条件列を更新しないようにする。
-
カーソル定義のFOR UPDATE OF句は,更新する列だけに指定する。
-
列(特にユニークインデクスの列)を同じ値で更新しないようにする。必ず更新する列だけをSET句で指定する。
-
カーソルを使って検索した行を更新,又は削除するときは,カーソル指定にFOR UPDATE句を指定する。
-
検索後に更新を予定している列がある場合,WITH EXCLUSIVE LOCKを指定する。
-
複数列に条件を付ける場合,複数列インデクスの適用を検討する(単一列インデクスでの検索範囲を広くしないため)。
-
WITHOUT LOCK NOWAITを使用した検索を検討する。
-
二つ以上の表をアクセスする場合,アクセス順序を統一する。また,A,Bの順でアクセスした場合,再度Aをアクセスしないようにする(Aの値は保存しておきます)。
-
LOCK TABLEを指定する。
-
INSERT文で行を挿入した直後に更新する場合,同一トランザクション内で処理をするようにする。
-
複数のUAPが,同一表に対して同時にANDの複数インデクス利用で更新する場合,システム定義のpd_work_table_optionオペランドに1を指定する。ANDの複数インデクス利用については,「クライアント環境定義の設定内容」のPDSQLOPTLVLを,pd_work_table_optionについては,マニュアル「HiRDB Version 9 システム定義」を参照してください。
-
インデクスキー値無排他を適用する。
-
スナップショット方式を適用した場合,意図しないデッドロックが発生するおそれがあるため,デッドロック発生時にリトライできるようなUAPを作成する。
以上のように,排他制御の範囲を考慮する以外にデッドロックを回避するためには,SQL文の種類とインデクスの種別による排他制御の順序について考慮する必要があります。詳細については,「SQL文の種類とインデクスの種別による排他制御の順序」を参照してください。
(7) プラグインが使用する論理ファイルのデッドロック回避策
プラグインが論理ファイルを使用する場合,更新操作の場合はEXモード,検索操作の場合はPRモードで,論理ファイル単位に排他を掛けます。
論理ファイルの排他は,データの値に関係なく操作発生を契機に実行されます。したがって,論理ファイルを使用するプラグイン定義のある列をアクセスする場合に,更新トランザクションを実行すると,該当する列を操作するほかのすべてのトランザクションとの間に,論理ファイルの競合が発生します。このため,論理ファイルを使用するプラグイン定義のある列を更新するプログラムは,できるだけ単独で実行するようにしてください。
-
デッドロック回避策1
LOCK TABLEを指定します。
-
デッドロック回避策2
論理ファイルがデッドロック資源となった場合,論理ファイルがデータ型プラグインのものか,又はインデクス型プラグインのものかを確認して,次に示す表を参照してください。
なお,デッドロックが発生した場合のデッドロック情報の出力内容については,マニュアル「HiRDB Version 9 システム運用ガイド」を参照してください。
- 排他情報:
-
種別000e −>論理ファイル
排他情報の先頭4バイト −>RDエリア番号
RDエリア番号からRDエリア名を調査します。
- RDエリアが抽象データ型のLOB属性格納用RDエリアの場合:
-
「行」として扱います。
- RDエリアがプラグインインデクスのRDエリアの場合:
-
「インデクスキー」として扱います。
- 注意
-
・プラグインインデクス検索の場合,NOWAIT指定であっても論理ファイルはPRモードで排他されます。
・データ操作時,LOCK TABLEの排他を掛けていても,論理ファイルはEX又はPRモードで排他されます。
・論理ファイルを使用するプラグイン定義のある列を複数定義した場合は,「デッドロック回避策2」では回避できません。「デッドロック回避策1」で回避してください。