web-dev-qa-db-ja.com

子テーブルに挿入すると、親テーブルがロックされなくなります

私はソフトウェア開発者であり、1〜2問クエリを記述しましたが、DBAではありません。したがって、間違った用語を使用したり、n00bのように聞こえたりする場合は、ご容赦ください。

最近、いくつかの大きな問題が発生しました。これは、サポートするインデックスのない外部キーにまで追跡されました。親テーブルから削除する場合は、子テーブルをロックする必要があります。これは理にかなっており、それは よく説明されています です。

これに飛び込んだとき、私はまた、子(孫)の子への挿入がそのような方法で子を使用することを発見しました。トランザクションが閉じられるまで、テーブルロックは取得できません。この振る舞いについて、私は多くのドキュメントを見つけることができません。私が得る最も近いのは このフォーラムの投稿 であり、基本的には「状況はそうである」と述べていますが、あまりコンテキストを提供していません。

それで、これを再処理する例を共有しましょう。従業員が実行するタスク(顧客への電話、製品の検索など)を含むタスクテーブルを取得しました。これらのタスクは、OrderLineを含む多数のエンティティにリンクできます。 OrderLineには、製品や数量などの注文の行が含まれています。次に、製品、注文、顧客、タスクなど、さまざまなエンティティで発生したすべての種類の変更に関する情報を含むログテーブルActionHistoryがあります。

つまり、基本的に、ActionHistoryはTaskの子であり、TaskLineはOrderLineの子です。この状況を作成するスクリプト:

-- CREATE Parent table (orderline)
create table TBL_OrderLine(
  OrderLineID integer
);
alter table TBL_OrderLine
  add constraint PK_OrderLine primary key (OrderLineID);

-- CREATE Child table (Task)
-- Task gets a new column, orderlineid, which has an FK, but is not indexed. 
-- Because of this, the table is locked
create table TBL_Task(
  TaskID integer,
  OrderLineID integer
);
alter table TBL_Task
  add constraint PK_Task 
    primary key (TaskID);
alter table TBL_Task
  add constraint FK_Task_OrderLine 
    foreign key (OrderLineID) 
    references TBL_OrderLine(OrderLineID);

-- CREATE Log table (child of child, Action history)
-- Action history (since only recently, by the way) has an FK to Task, which is propertly indexed.
create sequence SEQ_ActionHistoryID;

create table TBL_ActionHistory(
  ActionHistoryID integer,
  TaskID integer,
  Message varchar2(4000)
);
alter table TBL_ActionHistory
  add constraint PK_ActionHistory 
    primary key (ActionHistoryID);

alter table TBL_ActionHistory
  add constraint FK_ActionHistory_Task 
    foreign key (TaskID) 
    references TBL_Task(TaskID);

create index IDX_ActionHistory_TaskId on TBL_ActionHistory(TaskID);

-- Insert a bunch of orderlines
insert into TBL_OrderLine values (1);  
insert into TBL_OrderLine values (2);
insert into TBL_OrderLine values (3);

-- Insert a bunch of (unrelated) tasks.
insert into TBL_Task values (101, null);
insert into TBL_Task values (102, null);
insert into TBL_Task values (103, null);

commit;

TBL_Task.OrderLineIDのFKにはインデックスが付けられていません。
TBL_ActionHistory.TaskIDのFKには適切なインデックスがあります

セッション1では、アクション履歴に何か(まったく無関係)を追加します。 TaskIDもありません。

insert into TBL_ActionHistory(ActionHistoryID, Message) 
values (201, 'Something');

次に、セッション2で、注文明細を削除しようとします。

delete from TBL_OrderLine where OrderLineId = 2;

この2番目のステートメントは、最初のステートメントのトランザクションが終了するまで待機します。これは、ActionHistoryへの挿入が、他のセッションがTaskテーブルのロックを取得することをブロックしていることを示しています。

タスクテーブルでは何も変更されていないので、これには驚きました。チェックのためだけに使用することもできますが、その後は可能です。新しいActionHistoryレコードはTaskテーブルの行を参照していないため、データロックは必要ありません。なぜこれが発生しているのですか?

誰かが理論だけでなく、これを説明するOracleのドキュメントへの参照も提供できることを願っています。そしてもちろん、インデックスを追加するだけでなく、これをさらに微調整するための提案を受け入れます。

Oracle 11g(11.2.0.4.0)を使用しています。

4
GolezTrol

最初のステートメント、

insert into TBL_ActionHistory(ActionHistoryID, Message) 
    values (201, 'Something'); 

tBL_TASKおよびTBL_ACTIONHISTORYテーブルにTMロックを設定して、FK制約を適用します。 2番目のDELETEはTBL_ORDERLINEの行を正常にロックしますが、TBL_TASKテーブル全体もロックする必要があります(TBL_TASK.OrderLineIDにインデックスがないため)。 docs から:

ロックとインデックス付けされていない外部キー次の両方の条件に該当する場合、データベースは子テーブルの完全なテーブルロックを取得します。

子テーブルの外部キー列にインデックスが存在しません。

セッションは、親テーブルの主キーを変更する(たとえば、行を削除する、主キー属性を変更する)か、行を親テーブルにマージします。親テーブルへの挿入は、子テーブルのテーブルロックを取得しません。

Hr.departments表が、索引付けされていない外部キーdepartment_idを含むhr.employeesの親であるとします。図9-3は、departments表の部門60の主キー属性を変更するセッションを示しています。

2
a1ex07