使用 LIST-REF 复合分区的 DROP PARTITION 策略

use*_*116 5 oracle oracle-11g-r2 oracle-11g partitioning

我们有一个大型 Oracle 11gR2 实例,其中一些表最近在删除数据时变得很烦人。该数据库为大型优化集群提供动力,该集群从数据库获取输入元数据并存储文件(使用 11g 的 SecureFiles)和用于可视化的 1D-4D 输出数据。从数据库读取/写入非常快,在我们需要清理数据之前,一切都很好。

软件工程师被允许免费运行(阅读:我)并在没有任何分区的情况下设置这个系统,天真地假设删除会正常工作。我们的表结构如下,以前依赖于ON DELETE CASCADE删除每个Casewhere 的数据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 */
Run Code Online (Sandbox Code Playgroud)

扇出看起来像:1 个案例 = 50 次运行 = 2000 步 = 数据表中的 5-35M 行。数据表的总和约为 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)
Run Code Online (Sandbox Code Playgroud)

设置起来很轻松,改进了我们通常的可疑查询的许多计划,并使我们能够删除单个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;
Run Code Online (Sandbox Code Playgroud)

我们最终会发现数据表上的某些索引与 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);
Run Code Online (Sandbox Code Playgroud)

像这样的索引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
Run Code Online (Sandbox Code Playgroud)