學習dbms_parallel_execute包


一、簡介

  ORACLE11g R2版本的新特性之一就是引進了DBMS_PARALLEL_EXECUTE包,使用DBMS_PARALLEL_EXECUTE包批量並行遞增式的更新表。

  更多ORACLE11g新特性請參考:http://www.cnblogs.com/oracle-dba/articles/3632223.html
基本原理
  (1) 把數據集分割成小的塊(chunk),可基於rowid進行分塊,也可根據指定范圍的行數(rows)進行分塊。
  (2) 在每一個塊上以並行的方式應用update語句,在每個塊執行完成后,立即提交,原理是通過調用JOB進行並發操作。
好處在於
  (1) 在執行update操作時,僅僅鎖住一個chunk而非鎖住整個表;
  (2) 因為對每個chunk 執行完畢就提交,所以當update操作失敗后,之前變更的並不會回滾;
  (3) 減小回滾空間(undo)的使用;
  (4) 提高性能,可根據服務器的性能設置chunk_size與parallel_level的大小。
備注:parallel_level取決於CPU的個數,以及parallel_threads_per_cpu。

DBMS_PARALLEL_EXECUTE 使用三種將一個表的數據分割成多個chunk的方法:
  (1) CREATE_CHUNKS_BY_NUMBER_COL : 通過指定的字段來切割表
  (2) CREATE_CHUNKS_BY_ROWID : 通過ROWID來切割表,本文只介紹通過BY ROWID 進行分割
  (4) CREATE_CHUNKS_BY_SQL : 通過用戶提供的sql語句來切割表

二、實踐操作

  DBMS_PARALLEL_EXECUTE這個包操作起來比較簡單,大體步驟為:

  (1)創建任務task,即調用create_task()過程;

  (2)創建分塊規則,即調用create_chunk_by_rowid()或者create_chunk_by_number_col()等過程;

  (3)寫動態update的sql語句,復雜和簡單的均可,可以通過dbms_output.put_line()來驗證動態sql的正確性;

  (4)運行task任務,即調用run_task()過程;

  (5)監控task任務,可通過視圖user_parallel_execute_tasks和user_parallel_execute_chunks來監控task狀態;

  (6)task成功運行完畢后,將任務刪除,即調用drop_task()過程。

 2.1 准備工作

   給用戶授權

    調用dbms_parallel_execute包時,需要有執行create job的權限,因此需要給用於賦予create job權限。

      grant create job to username;

   也可把grant execute on dbms_scheduler to username;賦予某用戶。

 2.2 寫代碼

   實踐操作代碼,有改動,僅做參考。

CREATE OR REPLACE PROCEDURE Pro_Parallel_Exec_Update(i_Taskid IN VARCHAR2,
                                                       --任務ID  傳入是序列 para_task_seq.nextval
                                                       i_Inputdate IN DATE,
                                                       --日期
                                                       i_Tabname IN VARCHAR2,
                                                       --更新表名
                                                       i_Column IN VARCHAR2,
                                                       --更新列名
                                                       i_Tasktp IN VARCHAR2 DEFAULT 'BY ROWID'
                                                       -- 任務類型 未啟用的,可設置不能類型的 update
                                                       -- 如: 如不同類型的分塊方法  by rowid , by number_col ,by sql
                                                       ) IS
/*******************************************************************
   PROC_NAME : 並行更新過程,本例采用BY ROWID 方法
   EDIT_NAME : zzd @2014-03-26
   REQUIREMENT :
            1) ORACLE 11g R2 版本
            2) DBMS_PARALLEL_EXECUTE
            3) OWN CREATE JOB PRIVILEGE  eg: GRANT EXECUTE ON DBMS_SCHEDULER TO USER ;
            4) SET JOB_QUEUE_PROCESSES
    REFERENCE:
            (1) USER_PARALLEL_EXECUTE_CHUNKS
            (2) USER_PARALLEL_EXECUTE_TASKS
            (3) DBMS_PARALLEL_EXECUTE.DROP_TASK(TASK_NAME);
    ATTACH :
           (1) 分塊范圍 Chunk_Size 和並行度 Paralle_Level 可以根據服務器性能自行設置
           (2) DBMS_PARALLEL_EXECUTE 包三種 (BY ROWID,  BY NUMBER_COL ,BY SQL ) 分塊方法,原理差不多
           (3) 更多說明參考PLSQL Packages and Types Reference_11R2.pdf
   ********************************************************************/
   --- 任務名常量
   v_Taskname VARCHAR2(20) := 'UPDATE_TASK';
   --- schema常量
   v_Schema CONSTANT VARCHAR2(20) := 'USER';
   --- rowid范圍常量
   v_Cksize CONSTANT INT := 8000;
   --- 並行度大小
   v_Plevel CONSTANT INT := 10;
   --- 日期變量
   v_Inputdate VARCHAR2(64);
   --- 臨時動態sql 變量
   v_Sql_Stmt VARCHAR2(4000);
   --- 臨時動態insert_sql 變量
   v_Sql_Insert VARCHAR2(4000);
   --- 控制嘗試運行次數
   v_Trynum NUMBER;
   --- 返回運行狀態
   v_Status NUMBER;
BEGIN
   v_Trynum := 0;
   --- 日期轉換
   v_Inputdate := 'TO_DATE(''' || To_Char(i_Inputdate, 'YYYY-MM-DD') ||
                  ''',''' || 'YYYY-MM-DD' || ''')';
   --- 寫日志開始更新
   Dbms_Output.Put_Line('開始運行 ' || ' USER = ' || v_Schema ||
                        ' Table_Name= ' || i_Tabname ||
                        ' Update_Column = ' || i_Column || ' 任務類型為: ' ||
                        i_Tasktp);
   --- 注意這里只能用實體表作為臨時表,此處稱“中間表”否則在創建task 這步會把臨時表的數據情況,事務性和會話性臨時表均不可以
   --- 清空 中間表數據
   EXECUTE IMMEDIATE ' Truncate TABLE User_Table_Temp '; -- 注這個是個實體中間表
   --- 動態 insert sql 語句
   v_Sql_Insert := ' INSERT INTO User_Table_Temp(Table_Name, Status)
      SELECT a.Table_Name, a.Status
        FROM User_Tables a
       WHERE a.Table_Name LIKE ' || '''' ||
                   ' USER_NAME% ' || '''';

   --- 執行動態sql
   EXECUTE IMMEDIATE v_Sql_Insert;
   --- 測試動態sql
   Dbms_Output.Put_Line(v_Sql_Insert);

   ---  創建任務
   v_Taskname := v_Taskname || ' _ ' || i_Taskid;
   Dbms_Parallel_Execute.Create_Task(v_Taskname);

   --- 使用 ROWID 進行范圍分組
   Dbms_Parallel_Execute.Create_Chunks_By_Rowid(Task_Name => v_Taskname,
                                                Table_Owner => Upper(v_Schema),
                                                Table_Name => Upper(i_Tabname),
                                                By_Row => TRUE,
                                                Chunk_Size => v_Cksize);

   --- 動態update的sql 腳本
   v_Sql_Stmt := ' UPDATE /*+ ROWID (dda) */  ' || Upper(i_Tabname) || ' b 
              SET b.' || i_Column;

   v_Sql_Stmt := v_Sql_Stmt || ' = (SELECT a.TABLE_NAME|| A.STATUS
                FROM User_Table_Temp a
               WHERE b. Object_Name' ||
                 ' = a.TABLE_NAME
                 AND b.Created = ' || v_Inputdate || ') ';

   v_Sql_Stmt := v_Sql_Stmt || '
       WHERE EXISTS (SELECT 1
                FROM User_Table_Temp a
               WHERE b.Object_Name ' ||
                 ' = a.TABLE_NAME
                 AND b.Created = ' || v_Inputdate || ')';

   v_Sql_Stmt := v_Sql_Stmt || Chr(10) ||
                 'AND ROWID BETWEEN :Start_Id AND :End_Id ';

   --- 測試動態SQL
   Dbms_Output.Put_Line(v_Sql_Stmt);
   --- 執行並行更新
   Dbms_Parallel_Execute.Run_Task(Task_Name => v_Taskname,
                                  Sql_Stmt => v_Sql_Stmt,
                                  Language_Flag => Dbms_Sql.Native,
                                  Parallel_Level => v_Plevel);
   --- 更新結束
   Dbms_Output.Put_Line('完成運行 ' || ' USER = ' || v_Schema ||
                        ' Table_Name=  ' || i_Tabname ||
                        '  Update_Column = ' || i_Column || ' 任務類型為: ' ||
                        i_Tasktp);

   --- 如果 task任務出差,則恢復它,並重新執行,如此進行嘗試兩次
   v_Status := Dbms_Parallel_Execute.Task_Status(v_Taskname);

   WHILE (v_Trynum < 2 AND v_Status != Dbms_Parallel_Execute.Finished) LOOP
      v_Trynum := v_Trynum + 1;
      --- 恢復任務,嘗試繼續執行
      Dbms_Parallel_Execute.Resume_Task(v_Taskname);
      --- 獲取當前任務狀態
      v_Status := Dbms_Parallel_Execute.Task_Status(v_Taskname);
   
   END LOOP;
   -- 更新完畢后,刪除此任務
   Dbms_Parallel_Execute.Drop_Task(v_Taskname);

END Pro_Parallel_Exec_Update;

 三、相關視圖

  (1)USER_PARALLEL_EXECUTE_CHUNKS

  (2)USER_PARALLEL_EXECUTE_TASKS


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM