Как вызвать несколько процедур параллельно в Oracle 12c?

У меня есть следующая процедура,

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, но не смог понять, какой из них наиболее эффективен для моей цели оптимизации времени.

Пожалуйста, помогите мне понять, какой из них выбрать и как я могу это реализовать?


person padmanabh pande    schedule 19.06.2018    source источник


Ответы (2)


Я предлагаю использовать DBMS_PARALLEL_EXECUTE. Основной сеанс ожидает завершения дочерних параллельных сеансов. Есть удобные виды со статистикой и результатами - user_parallel_execute_chunks и user_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 ЗАДЕРЖКА ПРОИЗВОДИТЕЛЬНОСТИ ПОСЛЕ ОБНОВЛЕНИЯ ДО 11.2.0.4 Мы столкнулись с этим в версии 12.1, но есть исправления для исправления. Если это не исправлено, у вас есть шанс, что параллельная степень в конце будет меньше запрошенной (до 1).

person Maxim Borunov    schedule 19.06.2018

Я никогда не использовал первый вариант, который вы упомянули, но выбор между DBMS_JOB и DBMS_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;
/
person Littlefoot    schedule 19.06.2018
comment
DBMS_JOB — это устарело в версии 12.2 - person wolφi; 19.06.2018
comment
Ой. Спасибо, @wolφi. Будем надеяться, что 12c, упомянутый ОП, - это R1, тогда :) или этот вариант не сработает, что совсем неплохо - ему придется выбирать не из 3, а из 2 вариантов. Тогда есть 50% шанс выбрать один, но у вас есть 90% шанс выбрать неправильный (так говорит Мерфи). - person Littlefoot; 19.06.2018
comment
Спасибо за этот ответ, но, к сожалению, этот dbms_job.submit не вызывает разработку процедур. Эта процедура должна создать новую таблицу, которую я не увижу после вызова, хотя вызов процедуры engineering_all проходит успешно и без признаков ошибок. - person padmanabh pande; 19.06.2018
comment
Я использую оракул 12c R1. - person padmanabh pande; 19.06.2018
comment
Я протестировал его перед публикацией, так что да, он вызывает процедуру. Из-за выполнения через DBMS_JOB результат вы увидите не сразу. Проверьте это чуть позже (дело секунд, а не часов). - person Littlefoot; 19.06.2018