大規模なOracle 11gR2インスタンスがあり、 最近データを削除するときに煩わしくなっているテーブル があります。このデータベースは、DBから入力メタデータを取得し、ファイル(11gのSecureFilesを使用)およびビジュアライゼーションで使用される1D-4D出力データを格納する大規模な最適化クラスターを強化します。 DBからの読み取り/書き込みは非常に高速であり、データを一掃する必要があるまでは、本当に順調です。
ソフトウェアエンジニアは、フリーで実行することができ(read:me)、パーティション分割なしでこのシステムをセットアップし、単純に削除がうまくいくと仮定しました。テーブル構造は以下のとおりで、以前はCase
ごとにON DELETE CASCADE
のデータを削除するためにDeleteFlag = 1
に依存していました。
/* Metadata tables */
Case(CaseId, DeleteFlag, ...) On Delete Cascade CaseId
OptimizationRun(OptId, CaseId, ...) On Delete Cascade OptId
OptimizationStep(StepId, OptId, ...) On Delete Cascade StepId
/* Data tables */
Files(FileId, CaseId, Blob) /* deletes are near instantateous here */
/* Data per run */
OnedDataX(OptId, ...)
TwoDDataY1(OptId, ...) /* packed representation of a 1D slice */
/* Data not only per run, but per step */
TwoDDataY2(StepId, ...) /* packed representation of a 1D slice */
ThreeDDataZ(StepId, ...) /* packed representation of a 2D slice */
FourDDataZ(StepId, ...) /* packed representation of a 3D slice */
/* ... About 10 or so of these tables exist */
ファンアウトは次のようになります。1つのケース= 50回の実行= 2000ステップ=データテーブル全体で5〜3,500万行データテーブルの合計は〜500GBであり、1B行とは少し異なります。
いくつかの 姉妹サイトのヘルプで、私たちのセットアップで機能する可能性のあるパーティション設定スキームに到達しました (つまり、CaseId
によるパーティション設定)。これは、次のようにLIST-REFERENCE複合パーティションを使用します。
/* Metadata tables */
Case(CaseId, DeleteFlag, ...)
OptimizationRun(OptId, CaseId, ...)
Partition By List (CaseId)
( partition default_case values (default) )
/* Some PL/SQL is run when we add a Case to do:
* alter table OptimizationRun split partition ...
*/
OptimizationStep(StepId, OptId, ...)
Partition By Reference (Fk_OptId)
/* Data Tables */
OnedDataX(OptId, ...)
Partition By Reference (Fk_OptId)
...
FourDDataZ(StepId, ...) /* packed representation of a 3D slice */
Partition By Reference(Fk_StepId)
これはセットアップが簡単で、通常の疑わしいクエリの多くの計画が改善され、単一のDROP PARTITION
のケースのすべてのデータを削除できるようになりました。ここで問題が発生しました:インデックスの再構築!
削除を実行する現在の疑似PL/SQLは次のようになります。
for casedel in (select CaseId from Case where DeleteFlag = 1)
loop
execute immediate 'alter table OptimizationRun'
|| ' drop partition case_' || casedel.CaseId
|| ' update indexes';
end loop;
delete from Case where DeleteFlag = 1;
commit;
REFERENCEパーティションで使用される外部キーに関連しないデータテーブルのインデックスの一部に関するエラーが発生します。
これらの補助インデックスごとに実際にALTER INDEX blah REBUILD
状態を作成する必要がありますか、それともよりスマートな方法がありますか?
更新:読書を楽しむための実際のDDL
create table opt_case (
case_id number not null,
name varchar2(240) not null,
delete_date date,
constraint pk_optcase primary key (case_id) using index
);
create table opt_run (
opt_run_id number not null,
case_id number not null,
constraint pk_opt_run primary key (opt_run_id) using index,
constraint fk_opt_run_case
foreign key (case_id) references opt_case (case_id)
)
partition by list (case_id)
(
-- This is the catch-all partition. It is an error to insert into
-- this partition; it will be unused. Instead this is the
-- partition which is split when a new CASE_ID is added to
-- OPTCASE.
partition default_case values (default)
);
create index idx_fk_opt_run_case on opt_run (case_id);
create table opt_step (
opt_step_id number not null,
opt_run_id number not null,
step number not null,
step_value number(7,2),
constraint pk_opt_step primary key (opt_step_id) using index enable,
constraint fk_opt_step_opt_run
foreign key (opt_run_id) references opt_run (opt_run_id)
)
-- This is the golden ticket from here on out for partitioning.
-- Attach to the foreign key on OPT_RUN_ID to enjoy the
-- parent table partitioning on CASE_ID.
partition by reference (fk_opt_step_opt_run);
create index idx_fk_opt_step_opt_run on opt_step (opt_run_id);
-- One of the bigger data tables for reference
create table twod_data (
opt_step_id number not null,
spatial_coord_id number not null,
in_group number(1,0),
z_value number(3,0),
y_value number(5,3),
x_value number(5,1),
constraint pk_twod_data primary key (opt_step_id, spatial_coord_id),
constraint fk_twod_data_spat_coord
foreign key (spatial_coord_id)
references twod_spatial_cord (spatial_coord_id),
constraint fk_2d_data_opt_step
foreign key (opt_step_id) references opt_step (opt_step_id)
)
partition by reference (fk_2d_data_opt_step);
create index idx_fk_2d_data_opt_step on twod_data (opt_step_id);
idx_fk_2d_data_opt_step
のようなインデックスは、DROP PARTITION
句の後に再構築する必要があります。
データテーブルへの挿入は、面白くないシンプルなINSERT INTO blah
です。 Case
作成手順は次のようになります。
-- Add the CASE
insert into opt_case (name) values (p_CaseName) returning case_id into v_CaseId;
commit;
-- Add partition for the CASE
execute immediate 'alter table opt_run'
|| ' split partition default_case values (' || v_CaseId || ')'
|| ' into (partition case_' || v_CaseId ||', partition default_case)';
-- Because we split an empty partition, no index rebuild required
パーティションを削除または移動すると、グローバルインデックスが使用できなくなります。これは、パーティションに物理アドレスを指すエントリがあり、それが無効になっているためです。可能であれば、グローバルインデックスを使用しないようにしてください。あなたはそれらをローカルにすることでこれを行うことができます。
create index idx_fk_opt_run_case on opt_run (case_id) local;
トリックを行う必要があります。参照パーティション化を使用できる場合、リレーションシップはパーティション定義で既に定義されているため、テーブルに冗長データを追加する必要はありません。
例:
RONR SQL>create table parent
(id number not null
, x varchar2(10)
)
partition by list (id) (
partition par_1 values (1),
partition par_2 values (2)
); 2 3 4 5 6 7 8
Table created.
RONR SQL>create unique index pk_parent
on parent (id) local;
2
Index created.
RONR SQL>alter table parent
add CONSTRAINT pk_parent
PRIMARY KEY (id)
USING INDEX; 2 3 4
Table altered.
RONR SQL>create table child
( parent number not null
, y varchar2(10)
,
constraint fk_p_c
foreign key (parent) references parent(id))
partition by reference (fk_p_c); 2 3 4 5 6 7
Table created.
RONR SQL>insert into parent (id, x) values (1,'boe');
insert into parent (id, x) values (2,'oeps');
1 row created.
RONR SQL>
1 row created.
RONR SQL>select * from parent;
ID X
---------- ----------
1 boe
2 oeps
RONR SQL>insert into child (parent, y) values (1,'ggg');
1 row created.
RONR SQL>insert into child (parent, y) values (1,'ggg');
1 row created.
RONR SQL>insert into child (parent, y) values (2,'ppp');
1 row created.
RONR SQL>select * from child;
PARENT Y
---------- ----------
1 ggg
1 ggg
2 ppp
RONR SQL>select table_name, partition_name from user_tab_partitions;
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
PARENT PAR_1
PARENT PAR_2
CHILD PAR_1
CHILD PAR_2
RONR SQL>select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
PK_PARENT PAR_1 USABLE
PK_PARENT PAR_2 USABLE
RONR SQL>alter table parent drop partition par_1;
Table altered.
RONR SQL>select * from parent;
ID X
---------- ----------
2 oeps
RONR SQL>select * from child;
PARENT Y
---------- ----------
2 ppp
RONR SQL>select table_name, partition_name from user_tab_partitions;
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
PARENT PAR_2
CHILD PAR_2
RONR SQL>select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
PK_PARENT PAR_2 USABLE
使用できないインデックスはありません。私が見るのは、親パーティションを削除でき、これが子パーティションにカスケードすることです。これは役に立ちますか?