1

我有一个程序如下,

CREATE OR REPLACE PROCEDURE engineering_all ( idx IN NUMBER )
IS
tempstmt VARCHAR2(2000);
BEGIN
create_table_like( 'results_temp', 'results', 1);

tempstmt :=  'ALTER TABLE results_temp CACHE';
EXECUTE IMMEDIATE tempstmt;

engineering('CONSERVATIVE', idx);
engineering('INTERMEDIATE', idx);
engineering('AGGRESSIVE',   idx);
END;
/

对过程工程的三个调用是相互独立的,所以我想并行化它。我遇到了多种方式,例如 DBMS_PARALLEL_EXECUTE、DBMS_JOB、DBMS_SCHEDULER,但无法确定哪种方式对我的时间优化目标最有效。

请帮我弄清楚选择哪一个以及如何实施?

4

2 回答 2

2

我建议使用DBMS_PARALLEL_EXECUTE. 主会话等待子并行会话完成。有统计和结果的方便视图 -user_parallel_execute_chunksuser_parallel_execute_tasks. 我们在项目中使用它,觉得很方便。

有一点是,这个包需要分块,只能通过 rowid 或数字来完成。所以首先你必须创建一个接受数字的过程。这是适合您的情况:

create or replace procedure engineering_parallel(
  iLaunchType number, 
  idx         number
) 
is
begin
  if iLaunchType = 1 then
    engineering('CONSERVATIVE',idx);
  elsif iLaunchType = 2 then
    engineering('INTERMEDIATE',idx);
  elsif iLaunchType = 3 then
    engineering('AGGRESSIVE',idx);
  end if;
end;

在这里,您将找到一个在匿名 pl/sql 块中启动您的案例的示例,您可以轻松地将其转换为engineering_all过程:

declare
  -- idx parameter
  idx number := 0;
  -- unique parallel task name
  sTaskName varchar2(32767) := 'ENGINEERING-'||to_char(sysdate,'yyyy-mm-dd-hh24-mi-ss');
  -- this is where you store the query to split into chunks
  sChunkSQL varchar2(32767) := 'select level start_id, '||idx||' end_id'||chr(10)||
                               'from   dual connect by level <= 3';
  -- this is the procedure call
  sParallelSQL varchar2(32767) := 'begin engineering_parallel(:start_id,:end_id); end;';
  -- parallel degree
  iParalleDegree number := 3;
begin
  -- create a task
  DBMS_PARALLEL_EXECUTE.create_task(task_name => sTaskName);

  -- chunking
  DBMS_PARALLEL_EXECUTE.create_chunks_by_sql(
    task_name => sTaskName,
    sql_stmt  => sChunkSQL,
    by_rowid  => FALSE
  );

  -- launch. current session waits till all child parallel sessions are finished
  DBMS_PARALLEL_EXECUTE.run_task(
    task_name      => sTaskName,
    sql_stmt       => sParallelSQL,
    language_flag  => DBMS_SQL.NATIVE,
    parallel_level => iParalleDegree
  );

  dbms_output.put_line(
    'Job is finished.'||
    'Check user_parallel_execute_chunks, user_parallel_execute_tasks for the task '||
    sTaskName
  );

end;  

最后一点要考虑 - 检查您的版本是否包含错误 18966843的修复:DBMS_PARALLEL_EXECUTE PERFORMANCE DELAY AFTER UPGRADE TO 11.2.0.4 我们在 12.1 中遇到过它,但有修复它的补丁。如果它不是固定的,那么你有可能最后的平行度将低于要求(下降到 1)。

于 2018-06-19T12:29:58.850 回答
1

我从未使用过您提到的第一个选项,但是 - 在DBMS_JOBandDBMS_SCHEDULER之间进行选择DBMS_JOB更简单,所以我会选择它。以最简单的方式,过程可能如下所示:

CREATE OR REPLACE PROCEDURE engineering_all (idx IN NUMBER)
IS
   l_job      NUMBER;
   tempstmt   VARCHAR2 (2000);
BEGIN
   create_table_like ('results_temp', 'results', 1);
   tempstmt := 'ALTER TABLE results_temp CACHE';

   EXECUTE IMMEDIATE tempstmt;

   DBMS_JOB.submit (
      job         => l_job,
      what        => 'begin engineering(''CONSERVATIVE'', ' || idx || '); end;',
      next_date   => SYSDATE,
      interval    => NULL);

   DBMS_JOB.submit (
      job         => l_job,
      what        => 'begin engineering(''INTERMEDIATE'', ' || idx || '); end;',
      next_date   => SYSDATE,
      interval    => NULL);

   DBMS_JOB.submit (
      job         => l_job,
      what        => 'begin engineering(''AGGRESSIVE'', ' || idx || '); end;',
      next_date   => SYSDATE,
      interval    => NULL);

   COMMIT;
END;
/
于 2018-06-19T08:31:47.160 回答