用于DELETE性能问题的Oracle分区解决方案

use*_*116 8 oracle oracle11g

这是Strategy提高Oracle DELETE性能的后续问题.回顾一下,我们有一个大型DB,其中包含表示来自优化系统的1D到4D输出数据的表层次结构.读取和写入这些数据很快,并为我们的各种系统提供了一种方便的方法来利用这些信息.

但是,删除未使用的数据已经变成了熊.当前表层次结构如下.

/* 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)

我正在寻找的是一种分区Case数据的方法,这样我就可以删除与案例相关的分区来删除它的数据.理想情况下,OptimizationRun会有一个基于的间隔分区CaseId,这会过滤到它的子节点.但是,11g不支持INTERVAL和REF分区的组合.

我相当肯定基于数据库大小和表空间在ASSM中的要求,ENABLE ROW MOVEMENT是不可能的.可能RANGE分区OptimizationRun和其余的REF分区?

我猜这个策略我需要一个触发器来完成类似下面的操作:

CREATE OR REPLACE TRIGGER Case_BeforeInsert_MakePartitions
BEFORE INSERT
    ON Case
    FOR EACH ROW
DECLARE
    v_PartName varchar(64)       := 'CASE_OPTPART_' || :new.CaseId;
    v_PartRange Case.CaseId%type := :new.CaseId
BEGIN
    -- Take :new.CaseId and create the partition
    ALTER TABLE OptimizationRun
        ADD PARTITION v_PartName
        VALUES LESS THAN ( v_PartRange );
END;
Run Code Online (Sandbox Code Playgroud)

然后删除之前的必要触发器:

CREATE OR REPLACE TRIGGER Case_BeforeDelete_RemovePartitions
BEFORE DELETE
    ON Case
    FOR EACH ROW
DECLARE
    v_PartName varchar(64) := 'CASE_OPTPART_' || :old.CaseId;
BEGIN
    -- Drop the partitions associated with the case
    ALTER TABLE OptimizationRun
        DROP PARTITION v_PartName;
END;
Run Code Online (Sandbox Code Playgroud)

好主意?或者这是SNL Bad Idea Jeans商业广告中的一个想法吗?

更新,尺寸参考:

  • 1D数据表~1.7G
  • 2D数据表~12.5G
  • 3D数据表~117.3G
  • 4D数据表~315.2G

Vin*_*rat 4

我非常确定您使用分区来解决删除性能问题的方法是正确的。但是,我认为您无法将其与触发器混合使用。带有触发器的复杂逻辑一直困扰着我,但除此之外,您可能会遇到以下问题:

  • DDL 语句会破坏事务逻辑,因为 Oracle 在任何 DDL 语句之前执行当前事务的提交。
  • 幸运的是,您无法在触发器中提交(因为 Oracle 正在执行操作并且数据库未处于一致状态)。
  • 使用自治事务来执行 DDL 对于插入来说是一个(糟糕的?)解决方法,但不太可能适用于 DELETE,因为这可能会干扰 ON DELETE CASCADE 逻辑。

处理删除和创建分区的过程会更容易编码和维护,例如:

CREATE PROCEDURE add_case (case_id, ...) AS
BEGIN
   EXECUTE IMMEDIATE 'ALTER TABLE OptimizationRun ADD partition...';
   /* repeat for each child table */
   INSERT INTO Case VALUES (...);
END;
Run Code Online (Sandbox Code Playgroud)

关于删除分区,您必须检查这是否适用于引用完整性。在删除父子表关系中的父表分区之前,可能需要禁用外键约束。

另请注意,分区删除后全局索引将处于不可用状态。除非您在 drop 语句中指定 UPDATE GLOBAL ,否则您必须重建它们(显然这会自动重建它们,但会花费更多时间)。