web-dev-qa-db-ja.com

LIST-REF複合パーティション化を使用したDROP PARTITION戦略

大規模な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
5
user7116

パーティションを削除または移動すると、グローバルインデックスが使用できなくなります。これは、パーティションに物理アドレスを指すエントリがあり、それが無効になっているためです。可能であれば、グローバルインデックスを使用しないようにしてください。あなたはそれらをローカルにすることでこれを行うことができます。

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

使用できないインデックスはありません。私が見るのは、親パーティションを削除でき、これが子パーティションにカスケードすることです。これは役に立ちますか?

2
ik_zelf