2011-04-27 127 views
6

這是針對Strategy to improve Oracle DELETE performance的後續問題。回顧一下,我們有一個大型數據庫,其中包含表示層次結構的表示來自優化系統的1D到4D輸出數據。讀取和寫入這些數據的速度非常快,併爲我們各種系統利用這些信息提供了一個便利的途徑。用於DELETE性能問題的Oracle分區解決方案

但是,刪除未使用的數據已成爲一個熊。當前表層次結構如下。

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

我所尋找的是分割Case數據,這樣我可能會下降有關的情況下刪除其數據的分區的一種手段。理想情況下,OptimizationRun將有一個基於CaseId的間隔分區,並且這將會過濾到其子級。但是,11g不支持INTERVAL和REF分區的組合。

我很確定ENABLE ROW MOVEMENT基於數據庫大小以及表空間生活在ASSM中的要求而不存在問題。也許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; 

然後是必要的觸發之前刪除:

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; 

好主意?或者這是SNL Bad Idea Jeans商業廣告中的一個想法嗎?

更新,大小的參考

  • 1D數據表〜1.7G
  • 2D數據表〜12.5G
  • 三維數據表〜117.3G
  • 4D數據表〜315.2 G
+0

如何在刪除標記? (它是什麼邏輯)。基於一個日期也許? (老化記錄)。還有別的嗎?這可能有助於推導出最佳方法 – tbone 2011-04-27 19:46:06

+0

'DeleteFlag'由用戶設置,這會導致觸發器應用'SYSDATE + 14'的'DeleteDate',以防用戶想要撤銷其決定。 – user7116 2011-04-27 20:34:33

回答

2

我很確定你在分區的正確軌道上處理你的刪除p性能問題。但是,我認爲你不能將這與觸發器混合。與觸發複雜的邏輯一直困擾着我,但是除了這個你在這裏可能會遇到的問題:

  • 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; 

關於分區的下降,你必須檢查,如果這與參照完整性一起工作。在將父表分區放入父 - 子表關係之前,可能需要禁用外鍵約束。

另請注意,分區刪除後,全局索引將處於不可用狀態。你必須重建它們,除非你在drop語句中指定UPDATE GLOBAL(顯然這會自動重建它們,但會花費更多時間)。

+0

我可以在'Case'上做'AFTER INSERT'觸發器來添加分區,因爲'OptimizationRun'中的條目稍後添加。我不能完全切換到我不認爲的過程,因爲當前的應用程序不使用過程。試圖在數據庫中保留此更新。至於FK約束,如果我的子表在REF分區上,這應該是一個非問題,因爲它們將與父表一起被刪除。對? – user7116 2011-04-27 14:34:20

+0

觸發器需要聲明爲自治事務,它可以用於INSERT。至於刪除我還沒有使用REF分區,所以我不知道當你嘗試刪除父分區時會發生什麼。在嘗試使用DELETE CASCADE刪除行的同時執行分區刪除可能無法完美運行:) – 2011-04-27 14:40:56

+0

看起來我錯過了,我們顯然有過深思熟慮(閱讀:通過剪切運氣)創建新案例作爲存儲過程。現在要弄清楚子表分區。 – user7116 2011-04-27 14:48:24

1

不可能 - 您不能在行級觸發器中發出類似DDL的DDL。

[可能的設計問題的評論刪節,爲解決]

你有沒有考慮並行你的腳本?而不是依賴刪除級聯的清理程序,而是利用DBMS_SCHEDULER來並行化作業。您可以安全地對與依賴關係樹相同級別的表執行並行刪除操作。

begin 
    dbms_scheduler.create_program 
    (program_name => 'snapshot_purge_cases', 
    program_type => 'PLSQL_BLOCK', 
    program_action => 
     'BEGIN 
     delete from purge$Case; 
     insert into purge$Case 
     select CaseId 
      from Case 
      where deleteFlag = 1; 

     delete from purge$Opt; 
     insert into purge$Opt 
     select OptId 
      from OptimizationRun 
      where CaseId in (select CaseId from purge$Case); 

     delete from purge$Step; 
     insert into purge$Step 
     select StepId 
      from OptimizationStep 
      where OptId in (select OptId from purge$Opt); 

     commit; 
     END;', 
    enabled => true, 
    comments => 'Program to snapshot keys for purging';   
    ); 

    dbms_scheduler.create_program 
    (program_name => 'purge_case', 
    program_type => 'PLSQL_BLOCK', 
    program_action => 'BEGIN 
          loop 
          delete from Case 
          where CaseId in (select Case from purge$Case) 
          where rownum <= 50000; 
          exit when sql%rowcount = 0; 
          commit; 
          end loop; 
          commit; 
         END;', 
    enabled => true, 
    comments => 'Program to purge the Case Table' 
    ); 

    -- repeat for each table being purged 

end; 
/

只設置程序。接下來我們需要做的是建立一個工作鏈,以便我們可以把它們放在一起。

BEGIN 
    dbms_scheduler.create_chain 
    (chain_name => 'purge_case_chain'); 
END; 
/

現在我們做和以前使用程序的作業鏈步驟:

BEGIN 
    dbms_scheduler.define_chain_step 
    (chain_name => 'purge_case_chain', 
    step_name => 'step_snapshot_purge_cases', 
    program_name => 'snapshot_purge_cases' 
    ); 

    dbms_scheduler.define_chain_step 
    (chain_name => 'purge_case_chain', 
    step_name => 'step_purge_cases', 
    program_name => 'purge_case' 
    ); 

    -- repeat for every table 
END; 
/

現在我們必須鏈條步驟鏈接在一起。該職位會散開,像這樣:

  1. 快照CaseIdsOptIdsStepIds清除。
  2. 清除全部依賴Case.
  3. 清除Case.

的依賴OptimizationStep.

  • 清除所有表的表依賴於OptimizationRun.
  • 清除所有的表格,以便代碼將被:

    begin 
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'TRUE', 
        action  => 'START step_snapshot_purge_cases', 
        rule_name => 'rule_snapshot_purge_cases' 
        ); 
    
        -- repeat for every table dependent on OptimizationStep 
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'step_snapshot_purge_cases COMPLETED', 
        action  => 'START step_purge_TwoDDataY2', 
        rule_name => 'rule_purge_TwoDDataY2' 
        ); 
    
        -- repeat for every table dependent on OptimizationRun  
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'step_purge_TwoDDataY2 COMPLETED and 
            step_purge_ThreeDDataZ COMPLETED and 
            ... ', 
        action  => 'START step_purge_OnedDataX', 
        rule_name => 'rule_purge_OnedDataX' 
        ); 
    
        -- repeat for every table dependent on Case 
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'step_purge_OneDDataX COMPLETED and 
            step_purge_TwoDDataY1 COMPLETED and 
            ... ', 
        action  => 'START step_purge_Files', 
        rule_name => 'rule_purge_Files' 
        ); 
    
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'step_purge_Files   COMPLETED and 
            step_purge_OptimizationRun COMPLETED and 
            ... ', 
        action  => 'START step_purge_Case', 
        rule_name => 'rule_purge_Case' 
        ); 
    
        -- add a rule to end the chain 
        dbms_scheduler.define_chain_rule 
        (chain_name => 'purge_case_chain', 
        condition => 'step_purge_Case COMPLETED', 
        action  => 'END', 
        rule_name => 'rule_purge_Case' 
        ); 
    
    end; 
    /
    

    Ena竹葉提取作業鏈:

    BEGIN 
        DBMS_SCHEDULER.enable ('purge_case_chain'); 
    END; 
    /
    

    您可以手動運行鏈條:

    BEGIN 
        DBMS_SCHEDULER.RUN_CHAIN 
        (chain_name => 'chain_purge_case', 
        job_name => 'chain_purge_case_run' 
        ); 
    END; 
    /
    

    或者創建一個作業調度是:

    BEGIN 
        DBMS_SCHEDULER.CREATE_JOB (
        job_name  => 'job_purge_case', 
        job_type  => 'CHAIN', 
        job_action  => 'chain_purge_case', 
        repeat_interval => 'freq=daily', 
        start_date  => ... 
        end_date  => ... 
        enabled   => TRUE); 
    END; 
    /
    
  • +0

    我更新了設計以突出顯示實際FK結構中的錯誤。先生,我的歉意。 – user7116 2011-04-27 14:50:32

    +0

    沒問題。我也做了一些修改。 – 2011-04-27 15:53:01

    +0

    有趣的概念。我想這仍然會遇到通過並行性降低整個系統性能的問題,不是嗎?假設我獲得了2倍的並行度,CPU/IO的增加是否會損害其餘部分? – user7116 2011-04-27 16:56:58