データベーススペシャリスト 2017年 午後1 問02
トランザクションの排他制御に関する次の記述を読んで、設問1〜3に答えよ。
Y社は、オフィスじゅう器メーカである。 現在、在庫管理システムのアプリケーションプログラム (以下、APという) の改修を実施している。
〔在庫管理システムのテーブル〕
在庫管理システムの主なテーブル構造を、図1に示す。 各テーブルには主索引が定義されている。

〔在庫管理業務の概要〕
(1) 各地の生産拠点には、組立工場と、これに隣接する倉庫がそれぞれ一つ配置されている。
(2) 倉庫からの部品の出庫には、倉庫から隣接する組立工場に出庫する場合と、倉庫から他の生産拠点の倉庫に出庫する場合がある。
(3) 倉庫は、倉庫コードで一意に識別され、組立工場は、工場コードで一意に識別される。 生産拠点を識別するコードは存在しない。
(4) 定期便は、倉庫間で部品を配送する便であり、便番号で一意に識別される。
(5) 部品は、部品番号で一意に識別される。
(6) 部品の在庫は、倉庫と部品の組合せで、その数量をもつ。 倉庫内に存在する在庫を、倉庫内在庫と呼ぶ。 このうち、隣接する組立工場又は他の生産拠点の倉庫に向けて出庫対象となったものを、出庫対象在庫と呼ぶ。
(7) 出庫要求とは、倉庫に対して部品の出庫を要求することである。 “出庫” テーブルに出庫要求の内容が登録され、処理状況に ‘要求発生’ が記録される。 出庫番号は、出庫要求の発生順の一意な連番である。 組立工場が出庫要求する場合、出庫先倉庫コード及び出庫便番号の値はNULLとなり、出庫先工場コードが記録される。他の生産拠点の倉庫が出庫要求する場合、出庫先工場コードはNULLとなり、出庫先倉庫コードが記録され、出庫便番号には該当する定期便の便番号が記録される。
(8) 在庫引当とは、出庫要求に応じて倉庫内の在庫を引き当てることである。 在庫引当APは、毎日の業務中に定期的に実行され、その時点で登録されている出庫要求を処理する。 指定された倉庫コード、部品番号、出庫数量の出庫が可能かどうかチェックし、出庫可能であれば出庫対象在庫数量を更新する。 在庫引当が完了したら、処理状況は ‘引当実施’ に更新される。
(9) 出庫とは、出庫要求に従って、倉庫から部品を出すことである。 出庫は多頻度で行われるので、出庫ごとに在庫は更新されず、出庫確定APでまとめて更新される。
(10) 出庫確定APは、毎日の業務終了時に実行される。 “出庫” テーブルの処理状況が ‘引当実施’ のものを対象に、倉庫内在庫数量及び出庫対象在庫数量が更新され、処理が完了したら、処理状況は ‘出庫実施’に更新される。
(11) 入庫とは、他の生産拠点の倉庫で出庫された部品を倉庫に入れることである。入庫による在庫の更新は、在庫引当AP及び出庫確定APと同時に実行されることはない。
〔RDBMSの排他制御〕
(1) 在庫管理システムのRDBMSで選択できるトランザクションのISOLATIONレベルとその排他制御は、表1のとおりである。
ロックは行単位で掛ける。 共有ロックが掛かっている間、他のトランザクションからの対象行の参照は可能であり、更新は共有ロックの解放待ちとなる。 専有ロックが掛かっている間、他のトランザクションからの対象行の参照、更新は専有ロックの解放待ちとなる。

(2) 索引を使わずに、テーブルスキャンで全ての行に順次アクセスする場合、検索条件に合致するか否かにかかわらず全行がロック対象となる。 索引スキャンの場合、索引から読み込んだ行だけがロック対象となる。
〔分析機能の追加〕
適切な生産計画を立てるために、部品ごとに在庫数量、出庫数量の日別の推移状況を見たいという要望があり、そのための集計APを追加した。 集計APで実行するSQLの一部を、表2に示す。 SQL1は各部品の出庫年月日ごとの出庫数量を集計する。
また、SQL1では、出庫が全くない部品も集計対象とする。 SQL2は、各部品の倉庫間の出庫について、出庫年月日、出庫元倉庫、出庫先倉庫ごとに出庫数量を集計する。
〔在庫引当APの改修〕
在庫管理システムでは、トランザクションのISOLATIONレベルをREPEATABLE READとして設計、運用していた。 システムの改修に当たり、在庫引当APのトランザクションのISOLATIONレベルをREAD COMMITTEDに変更することにした。ISOLATIONレベルの変更で問題が発生しないように在庫引当APを改修した。
改修前の在庫引当APは図2, 改修後の在庫引当APは図3のとおりである。 これらのAPの実行に先立って、“出庫” テーブルの処理状況が ‘要求発生’ の行を抽出し、出庫先倉庫ごとに分割したファイルを作成する。 それぞれのファイルのレコードは、出庫番号順に記録されている。 作成したファイルを入力として、在庫引当APを並列実行している。 在庫引当APは、入力ファイルのレコードごとに繰り返し実行される。
なお、図2,3中のホスト変数hv0は出庫番号、hv1は出庫元倉庫コード、hv2は部品番号、hv3は出庫数量を表す。 hv4とhv5は、検索結果を返す出力ホスト変数を表す。
〔出庫確定APの改修 〕
出庫確定APの処理に掛かる時間を短縮するために、出庫確定APを並列に多重プロセスで実行するように変更することにした。 “出庫” テーブルの出庫番号の値の範囲指定で各プロセスに均等に配分して、REPEATABLE READで並列実行する。 出庫確定APの概要は、図4のとおりである。
設問1:
問題文を見る〔分析機能の追加〕について、表2中の(a)〜(e)に入れる適切な字句を答えよ。
模範解答
a:SUM(S.出庫数量)
b:LEFT OUTER JOIN
c:GROUP BY B.部品番号, S.出庫年月日
d:出庫便番号
e:NOT NULL
解説
解答の論理構成
-
出庫数量の集計列
【問題文】では「SQL1は各部品の出庫年月日ごとの出庫数量を集計する。」とあります。したがって (a) には数量の合計を返す SUM(S.出庫数量) が入ります。 -
結合種別の決定
「SQL1 では、出庫が全くない部品も集計対象とする。」と指示されています。これは基準表(在庫)に存在するが従表(出庫)に行が存在しないケースも取り込む必要があるため、(b) には LEFT OUTER JOIN を置きます。 -
集計単位の指定
集計単位は「各部品の出庫年月日ごと」ですから、(c) の GROUP BY 句には B.部品番号,S.出庫年月日 を列挙します。 -
倉庫間出庫の抽出条件
SQL2について【問題文】には「倉庫間の出庫」を対象とするとあります。倉庫間出庫は便が割り振られるケースであり、便番号がNULLでない行が条件になります。従って (d) は 出庫便番号、(e) は NOT NULLです。
誤りやすいポイント
- 内部結合 INNER JOIN を選んでしまい、出庫が無い部品が欠落する。
- GROUP BY に B.倉庫コード を含めてしまうなど、要件外の列を入れて集計粒度を細かくしてしまう。
- (d) IS (e) を「出庫便番号 IS NULL」と誤記し、倉庫間ではなく工場向け出庫を抽出してしまう。
- SUM 句でエイリアスを付け忘れ、後続の帳票処理で列名が合致しなくなる。
FAQ
Q: LEFT OUTER JOIN と RIGHT OUTER JOIN のどちらを使っても良いですか?
A: 基準表を「在庫」にしたいので LEFT OUTER JOIN が妥当です。RIGHT を用いる場合はテーブルの左右を入れ替える必要があり、可読性が下がります。
A: 基準表を「在庫」にしたいので LEFT OUTER JOIN が妥当です。RIGHT を用いる場合はテーブルの左右を入れ替える必要があり、可読性が下がります。
Q: 出庫便番号 がNULLで無いことだけで倉庫間出庫と断定できますか?
A: 【問題文】には「他の生産拠点の倉庫が出庫要求する場合、… 出庫便番号には該当する定期便の便番号が記録される。」と明記されています。よって NOT NULL条件で倉庫間出庫を抽出できます。
A: 【問題文】には「他の生産拠点の倉庫が出庫要求する場合、… 出庫便番号には該当する定期便の便番号が記録される。」と明記されています。よって NOT NULL条件で倉庫間出庫を抽出できます。
Q: SQL1の SUM(S.出庫数量) は0行の場合どうなりますか?
A: LEFT OUTER JOIN で結合できない行は NULL を返し、SUM は NULL を結果にします。必要に応じて COALESCE で 0 に置き換える実装も考えられます。
A: LEFT OUTER JOIN で結合できない行は NULL を返し、SUM は NULL を結果にします。必要に応じて COALESCE で 0 に置き換える実装も考えられます。
関連キーワード: 外部結合、集約関数、NULL判定、グループ化、結合条件
設問2:〔在庫引当APの改修〕について、(1)〜(3)に答えよ。
問題文を見る(1)図2の改修前の在庫引当APが、REPEATABLE READで複数同時に実行されるとデッドロックが発生するおそれがある。 どのような場合にデッドロックが発生するか、AP間のSQLの実行状況を、図2中の丸数字を用いて60字以内で述べよ。
模範解答
“在庫”テーブルの同じ行に対して、先行するAPの①と②の間で、後続のAPの①が実行された場合
解説
解答の論理構成
- ①の役割
【問題文】「データ参照時に共有ロックを掛け、トランザクション終了時に解放する。」
→ 図2の①は SELECT なので同一行に共有ロックが掛かり、トランザクション中保持され続けます。 - ②の役割
【問題文】「データ更新時に専有ロックを掛け、トランザクション終了時に解放する。」
→ 図2の② UPDATE では専有ロックへの昇格が必要です。 - 複数APの同時実行
- 先行AP:①で共有ロック取得 →②で専有ロック待ち
- 後続AP:①で共有ロック取得(先行APの②がまだ実行前なので取得可)→②で専有ロック待ち
- 循環待ちの成立
先行APは後続APの共有ロック解放を、後続APは先行APの共有ロック解放を待つ状態となり、デッドロックが完成します。 - したがって模範解答「“在庫”テーブルの同じ行に対して、先行するAPの①と②の間で、後続のAPの①が実行された場合」が成立します。
誤りやすいポイント
- SELECT はロックを掛けないと誤解し、「共有ロック」を忘れる。
- 「READ COMMITTED」なら共有ロックはすぐ解放されるが、設計は「REPEATABLE READ」である点を見落とす。
- ファイル分割でAPを並列にしているから同じ行には触れないと決めつけてしまう。
- ロックの粒度が「行単位」であることを意識せず、テーブル全体で考えてしまう。
FAQ
Q: ②がロック昇格待ちになる理由は何ですか?
A: UPDATE は共有ロックでは実行できず、同じ行への「専有ロック」が必要だからです。共有ロックが残っている限り昇格できません。
A: UPDATE は共有ロックでは実行できず、同じ行への「専有ロック」が必要だからです。共有ロックが残っている限り昇格できません。
Q: 「READ COMMITTED」に変更すればデッドロックはなくなりますか?
A: ①の共有ロックが参照終了時に解放されるためデッドロックの確率は下がりますが、タイミングによっては依然として発生し得ます。そこで図3のようにカーソル FOR UPDATE を用いてロック取得の粒度とタイミングを調整します。
A: ①の共有ロックが参照終了時に解放されるためデッドロックの確率は下がりますが、タイミングによっては依然として発生し得ます。そこで図3のようにカーソル FOR UPDATE を用いてロック取得の粒度とタイミングを調整します。
関連キーワード: 共有ロック、専有ロック、ロック昇格、デッドロック、トランザクション隔離レベル
設問2:〔在庫引当APの改修〕について、(1)〜(3)に答えよ。
問題文を見る(2)図2の改修前の在庫引当APが、READ COMMITTEDで同じ倉庫の同じ部品に対して複数同時に実行されると、在庫数量が不正になるおそれがある。 在庫数量が不正になるAPの実行状況を図5に示す。 不正になるのは、AP2の①〜④の各SQLが、t2, t4, t6, t8のどの時間帯で実行された場合か、該当する時間帯に①〜④を記入せよ。 ここで、一つの時間帯に複数のSQLを実行できる。
また、この状況が発生した場合の、在庫数量が不正とは具体的にどのような状態か、30字以内で述べよ。


模範解答
実行状況:


状態:出庫対象在庫数量が倉庫内在庫数量を超える。
解説
解答の導き方
結論(図5に記入する内容)
AP2の ① はt2、②・③・④ はt8に実行される場合に在庫数量が不正になります。
AP2の ① はt2、②・③・④ はt8に実行される場合に在庫数量が不正になります。
理由を順を追って説明します。
-
図2の処理は要約すると次のとおりです。特にチェックと更新の順序を確認します。
「① SELECT 倉庫内在庫数量、出庫対象在庫数量 INTO :hv4, :hv5 FROM 在庫 WHERE 倉庫コード = :hv1 AND 部品番号 = :hv2」
「hv4 − hv5とhv3を比較し、出庫が可能な場合だけ以降を実行する。」
「② UPDATE 在庫 SET 出庫対象在庫数量 = 出庫対象在庫数量 + :hv3 WHERE 倉庫コード = :hv1 AND 部品番号 = :hv2」
「③ UPDATE 出庫 SET 処理状況 = '引当実施' WHERE 出庫番号 = :hv0」
「④ COMMIT」 -
RDBMS の排他制御(問題文の表)で、READ COMMITTED は「データ参照時に共有ロックを掛け、参照終了時に解放する。データ更新時に専有ロックを掛け、トランザクション終了時に解放する。」とあります。つまり SELECT(参照)は文終了で共有ロックを解放し、UPDATE(更新)はコミットまで専有ロックを保持します。
-
図5のAP1の実行順はt1:①、t3:②、t5:③、t7:④ です。これを基に並列実行の干渉を考えます。
- t1(AP1の ①)でAP1は現在の倉庫内在庫数量(hv4)と出庫対象在庫数量(hv5)を読み取り、文終了とともに共有ロックは解放されます(READ COMMITTEDの性質)。
- したがって AP2 は t2 で同じ SELECT(AP2 の ①)を実行でき、同じ古い hv4, hv5 の値を取得して「hv4 − hv5 と hv3 の比較」を通過します。ここで両者は同値のチェックに合格して次に進む前提を持ちます。
- t3(AP1 の ②)で AP1 が UPDATE を実行すると専有ロックを取得し、出庫対象在庫数量を増やします。この専有ロックは t7(COMMIT)まで解かれません。
- t4(AP2 の ②)で AP2 が UPDATE を発行すると、AP1 の専有ロックのためにブロックされ、実際の更新は AP1 の COMMIT 後にしか実行できません。したがって AP2 の ②〜④ は実質的に t8 以降に行われます。
- 結果としてAP1とAP2はそれぞれhv3を加算する更新を行い、両者の合算が元の倉庫内在庫数量を超えることがあり得ます。つまり出庫可否の判定は古い読み取り結果に基づいており、実際の更新は直列化されても「二重引当て(過剰引当て)」が発生します。
-
以上より、図5の空欄にはAP2の ① をt2に、②・③・④ をt8に記入するのが正解です。
不正となる具体的状態(設問の回答)
出庫対象在庫数量が倉庫内在庫数量を超える。
出庫対象在庫数量が倉庫内在庫数量を超える。
誤りやすいポイント
- 図2 の SELECT に FOR UPDATE があると誤解すること。図2 の SELECT には FOR UPDATE が無く、READ COMMITTED では文終了時に共有ロックが解放されます。図3 の注記にある「在庫カーソルに FOR UPDATE を指定した場合、FETCH された行に専有ロックが掛かる。」と区別して理解すること。
- UPDATE の式とチェックの参照対象を混同すること。チェックはローカル変数 hv4, hv5 に基づくが、更新は「出庫対象在庫数量 = 出庫対象在庫数量 + :hv3」として DB 上の現在値に加算するため、事前チェックと更新の間に他トランザクションが介在すると過剰引当てになる。
- READ COMMITTEDとREPEATABLE READの違いを逆に覚えること。READ COMMITTEDは参照終了時に共有ロックを解放し、REPEATABLE READはトランザクション終了時まで保持する点が鍵である。
- ブロックの発生位置の取り違え:SELECT はブロックされないが、UPDATE は専有ロックのためブロックされ得る。結果としてブロックされる SQL は UPDATE 側であることを押さえる。
FAQ
Q: なぜREPEATABLE READのときは問題にならなかったのですか?
A: REPEATABLE READ は「データ参照時に共有ロックを掛け、トランザクション終了時に解放する」ため、AP1 の SELECT がトランザクション終了まで共有ロックを保持します。共有ロックが存在する間に他トランザクションが専有ロック(UPDATE)を取ろうとすると待ちになるため、事前に同じチェックを通過した複数のトランザクションが次々に更新する事態が起きにくく、過剰引当てが回避されます。
A: REPEATABLE READ は「データ参照時に共有ロックを掛け、トランザクション終了時に解放する」ため、AP1 の SELECT がトランザクション終了まで共有ロックを保持します。共有ロックが存在する間に他トランザクションが専有ロック(UPDATE)を取ろうとすると待ちになるため、事前に同じチェックを通過した複数のトランザクションが次々に更新する事態が起きにくく、過剰引当てが回避されます。
Q: 図3 のカーソル FOR UPDATE はどう防止するのですか?
A: 図3 はカーソルを「FOR UPDATE」で宣言し、FETCH 時に該当行に専有ロックを掛けます(注記の通り)。これにより SELECT(チェック)直後にその行が専有ロックで保護され、他トランザクションの UPDATE をブロックしてチェックと更新を擬似的に原子的にします。結果として過剰引当てを防げます。
A: 図3 はカーソルを「FOR UPDATE」で宣言し、FETCH 時に該当行に専有ロックを掛けます(注記の通り)。これにより SELECT(チェック)直後にその行が専有ロックで保護され、他トランザクションの UPDATE をブロックしてチェックと更新を擬似的に原子的にします。結果として過剰引当てを防げます。
Q: 実装上の別解(Lock以外)はありますか?
A: はい。典型的には次の方法があります。
A: はい。典型的には次の方法があります。
- アトミックな更新クエリを用いる:例えば 「UPDATE 在庫 SET 出庫対象在庫数量 = 出庫対象在庫数量 + :hv3 WHERE 倉庫コード = :hv1 AND 部品番号 = :hv2 AND 倉庫内在庫数量 - 出庫対象在庫数量 >= :hv3」 を実行し、更新件数が 0 のときは引当失敗とする。これでチェックと更新を一文で行えます。
- 楽観ロック(バージョン番号)を使う:UPDATE 時にバージョンを条件にして、失敗したら再試行する。
- トランザクション分離レベルを上げる(REPEATABLE READ等)か、図3のように明示的ロックを使う。
関連キーワード: 共有ロック、専有ロック、READ COMMITTED、REPEATABLE READ、SELECT FOR UPDATE, ロストアップデート
設問2:〔在庫引当APの改修〕について、(1)〜(3)に答えよ。
問題文を見る(3)図3中の(f)に入れる適切な字句を答えよ。
模範解答
f:CURRENT
解説
解答の論理構成
-
図3の該当部分を確認UPDATE 在庫 SET 出庫対象在庫数量 = 出庫対象在庫数量 + :hv3 WHERE (f) OF 在庫カーソルここで (f) は、直前に FETCH した行だけを更新対象とするための句です。
-
カーソル宣言の条件DECLARE 在庫カーソル CURSOR FOR SELECT ... FROM 在庫 ... FOR UPDATE問題文には「在庫カーソルに FOR UPDATE を指定した場合、FETCH された行に専有ロックが掛かる。」とあります。FOR UPDATE が付いたカーソルで直前にフェッチした行を特定する標準句は WHERE CURRENT OF です。
-
標準SQLルール
- WHERE CURRENT OF <cursor_name> は、そのカーソルで最後にフェッチした行のみを対象に UPDATE または DELETE を行う。
- これにより主キーを再指定せずとも対象行を一意に特定でき、排他ロックも保持される。
-
以上より (f) にはCURRENTを補完するのが正答となります。
誤りやすいポイント
- WHERE ROWID = :host_var のように行識別子で更新しようとすると、ロックを保持する保証がなく READ COMMITTED では競合を招く恐れがあります。
- CURRENT だけではなく WHERE CURRENT OF <カーソル名> が完全形であることを忘れ、句全体を記述してしまう。
- FOR UPDATE がないカーソルで WHERE CURRENT OF を使用できると誤解する(多くの RDBMS でエラー)。
FAQ
Q: WHERE CURRENT OF 句を使うとき、主キー索引は不要ですか?
A: 不要です。カーソルが保持しているカーソルポインタ情報で行を特定できるため、主キーや索引を再指定する必要はありません。
A: 不要です。カーソルが保持しているカーソルポインタ情報で行を特定できるため、主キーや索引を再指定する必要はありません。
Q: READ COMMITTEDに変更しても一貫性は保てますか?
A: はい。FOR UPDATE によりフェッチ時点で対象行に専有ロックが掛かり、COMMIT まで解放されないため、同一行に対する他プロセスの更新は待機します。
A: はい。FOR UPDATE によりフェッチ時点で対象行に専有ロックが掛かり、COMMIT まで解放されないため、同一行に対する他プロセスの更新は待機します。
Q: CURRENT OFとCURRENTだけの違いは?
A: UPDATE ... WHERE CURRENT OF <カーソル名> が正式構文です。本設問ではテンプレート中に OF 在庫カーソル が既に書かれているため、空所 (f) には「CURRENT」のみを挿入します。
A: UPDATE ... WHERE CURRENT OF <カーソル名> が正式構文です。本設問ではテンプレート中に OF 在庫カーソル が既に書かれているため、空所 (f) には「CURRENT」のみを挿入します。
関連キーワード: カーソル操作、WHERE CURRENT OF, FOR UPDATE, 行ロック、READ COMMITTED
設問3:〔出庫確定APの改修〕について(1)、(2)に答えよ。
問題文を見る(1)並列に実行するように変更したが、スループットはさほど向上しなかった。ボトルネックはどこにあるかを説明する次の記述について、(ア)〜(エ) に入れる適切な字句を答えよ。
複数の出庫を並列に処理することになるが、同じ(ア)と(イ)に対する出庫が複数存在するので、“(ウ)”テーブルの更新で(エ)が発生する。
(ア、イは順不同)
模範解答
ア:倉庫コード
イ:部品番号
ウ:在庫
エ:ロックの解放待ち
解説
解答の論理構成
- 出庫確定APの処理
【問題文】図4 2-1, 2-2 「“在庫”テーブルの倉庫内在庫数量を更新」「“在庫”テーブルの出庫対象在庫数量を更新」
→ 同一行(キーは「倉庫コード、部品番号」)を更新する。 - ロック仕様
【問題文】表1 REPEATABLE READ「データ更新時に専有ロックを掛け、トランザクション終了時に解放する。」
→ 更新中の行は他トランザクションから参照も更新もできずロック待ちになる。 - 並列化の影響
出庫を範囲分割して並列実行しても、各プロセスが同じ「倉庫コード」「部品番号」の行を更新するケースが頻発。
その結果 “在庫” 行の専有ロック解放を待つ ロックの解放待ち が多数発生し、スループットが頭打ちになる。 - よって空欄には
(ア)倉庫コード (イ)部品番号 (ウ)在庫 (エ)ロックの解放待ち
が入る。
誤りやすいポイント
- 「範囲指定で出庫番号を分割したから競合しない」と思い込み、キー列の衝突を見落とす。
- 行ロックはREPEATABLE READ固有の問題だと誤解し、READ COMMITTEDなら解放待ちが起きないと判断する。
- 在庫テーブルに索引を追加すれば解決するという早合点(索引は探索効率でありロック待ちとは別問題)。
FAQ
Q: UPDATE が 2 回(2-1, 2-2)あるのに 1 行ロックで済むのですか?
A: はい。同一トランザクション内で同じ行を連続更新しても、最初の UPDATE で掛けた専有ロックが COMMIT まで保持されます。追加の UPDATE は既に自トランザクションのロック下にあるため新たなロック待ちは発生しません。
A: はい。同一トランザクション内で同じ行を連続更新しても、最初の UPDATE で掛けた専有ロックが COMMIT まで保持されます。追加の UPDATE は既に自トランザクションのロック下にあるため新たなロック待ちは発生しません。
Q: READ COMMITTEDに変更すればスループットは上がりますか?
A: 今回は出庫確定APを REPEATABLE READ で動かす設計です。READ COMMITTEDでも更新行への専有ロックはトランザクション終了時まで保持されるため、同じ行を競合更新する限り根本的なボトルネックは解消されません。
A: 今回は出庫確定APを REPEATABLE READ で動かす設計です。READ COMMITTEDでも更新行への専有ロックはトランザクション終了時まで保持されるため、同じ行を競合更新する限り根本的なボトルネックは解消されません。
関連キーワード: 排他制御、行ロック、ロック待ち、並列処理、ボトルネック
設問3:〔出庫確定APの改修〕について(1)、(2)に答えよ。
問題文を見る(2)(1)のボトルネックを解消するためには出庫確定APをどのように変更する必要があるかを説明する次の記述について、(オ)〜(ケ)に入れる適切な字句を答えよ。
出庫番号ではなく、(オ)と(カ)の組合せの値の範囲指定で各プロセスに配分するように変更する。 また、図4の1で、出庫番号の昇順ではなく、(オ)と(カ)の昇順に処理を行うように変更する。 “(キ)”テーブルの“(オ)”列と“(カ)”列に、複数列索引を定義しておく。
なお、この索引を定義していない場合、自プロセスの対象行かを判定するための参照が(ク)となるので、他プロセスが(ケ)した行を参照しようとしてロックの解放待ちとなり、別のボトルネックが生じる。(オ、カは順不同)
模範解答
オ:出庫元倉庫コード
カ:部品番号
キ:出庫
ク:テーブルスキャン
ケ:更新 又は 専有ロック
解説
解答の論理構成
- 更新競合の原因
出庫確定APは図4で「倉庫内在庫数量」「出庫対象在庫数量」を更新します。これらの更新条件は〈倉庫コード=出庫元倉庫コード〉かつ〈部品番号〉です。よって同じ倉庫・部品を別プロセスが同時に扱えば、在庫行に対する専有ロック競合が起こります。 - 適切な分割キー
競合を無くすには「同じ在庫行を複数プロセスが更新しない」よう範囲分割します。したがってプロセス配分の主キーは
• 出庫元倉庫コード(オ)
• 部品番号(カ)
の組合せとなります。 - 索引の必要性
問題文(1)のRDBMS仕様より「索引を使わずに…テーブルスキャン…全行がロック対象」と明記されています。複合索引を定義しない場合、各プロセスは自分の担当範囲を判定するために全行を読み、共有ロックであっても他プロセスが取得した専有ロックに衝突して待機します。 - ボトルネックの連鎖
ロック待ちは「他プロセスが更新(ケ)した行」を参照しようとした瞬間に発生します。これが新たなボトルネックになるため、複合索引の作成は必須です。 - まとめ
以上より、空欄は
オ:出庫元倉庫コード
カ:部品番号
キ:出庫
ク:テーブルスキャン
ケ:更新 又は 専有ロック
が正答となります。
誤りやすいポイント
- 出庫番号で分割しても在庫表を更新する条件列が変わらないことを見落とす。
- 「参照だけならロック競合しない」と思い込み、テーブルスキャン時の共有ロック増大を軽視する。
- 索引を作ってもロックそのものが無くなるわけではない事実を混同する。
FAQ
Q: 「出庫先倉庫コード」ではダメなのですか?
A: 在庫の更新条件は「出庫元倉庫コード」と「部品番号」です。出庫先倉庫コードで分割しても同じ倉庫・部品行を複数プロセスが更新する可能性が残り、ロック競合は解消しません。
A: 在庫の更新条件は「出庫元倉庫コード」と「部品番号」です。出庫先倉庫コードで分割しても同じ倉庫・部品行を複数プロセスが更新する可能性が残り、ロック競合は解消しません。
Q: READ COMMITTEDに下げればロック待ちは軽減しますか?
A: 出庫確定APはREPEATABLE READが指定されています。READ COMMITTEDに変更しても更新完了までは専有ロックが保持されるため、今回の競合(更新×参照)は依然として発生します。分割キーの見直しが根本解決です。
A: 出庫確定APはREPEATABLE READが指定されています。READ COMMITTEDに変更しても更新完了までは専有ロックが保持されるため、今回の競合(更新×参照)は依然として発生します。分割キーの見直しが根本解決です。
関連キーワード: 排他ロック、複合索引、テーブルスキャン、並列処理







