Есть ли лучший способ избежать выполнения процесса более одного раза в Oracle? - PullRequest
14 голосов
/ 22 марта 2012

Допустим, у меня есть procedure с именем myproc.Это сложный процесс, и я не могу позволить двум экземплярам одновременно выполнять процедуру.

На самом деле я делаю это с помощью dbms_application_info.set_module:

procedure start_process is
begin
  dbms_application_info.set_module('myproc', 'running');
end;

и проверяю перед запуском процесса:

select 'S'
  from v$session v
 where v.module = 'myproc'
   and v.action = 'running';

Есть ли лучший способ проверить это на уровне базы данных?

Ответы [ 2 ]

10 голосов
/ 22 марта 2012

Используйте dbms_lock.allocate_unique вместе с dbms_lock.request . примечания по использованию гласят:

Первый сеанс, который вызывает ALLOCATE_UNIQUE с новым именем блокировки, вызывает создание уникального идентификатора блокировки и его сохранение в таблице dbms_lock_allocated.Последующие вызовы (обычно в других сеансах) возвращают ранее созданный идентификатор блокировки.

Я думаю, что это может быть то, что вы ищете.

1 голос
/ 22 марта 2012

Вы можете создать таблицу processes. Вы также гарантируете, что каждый процесс имеет своего рода уникальный идентификатор - например, хэш owner, object_name из dba_objects, чтобы вы могли динамически создавать его в своем пакете.

Затем вы создаете функцию для индивидуальной блокировки каждой строки при запуске процесса.

Как отметил @Sergio в комментариях, это не сработает, если по какой-то причине вам понадобится совершить коммит в середине процесса - если, конечно, вы не переизбрали после каждого коммита.

function locking ( Pid ) return number is

   l_locked number := 0;

begin

   select 1
     into l_locked
     from processes
    where id = Pid
         -- exit immediately if the proc is running
      for update nowait
          ;

   return l_locked;

   exception when others then
      return 0;

end;

Это имеет преимущество, заключающееся в том, что вы блокируете эту строку в processes до тех пор, пока не завершится сеанс, в котором в данный момент выполняется ваша процедура.

Затем оберните это в своей процедуре:

-- if we failed to lock locking will have thrown an error
-- i.e. we have 0 here.
if locking( 123 ) = 0 then
   exit;
end if;

Пока каждая процедура имеет уникальный идентификатор - важный бит - ваша процедура будет завершена корректно.


Это может не применяться в вашей ситуации, но мой обычный способ сделать это - использовать mod. Хотя он не останавливает 2 запущенных процесса, он гарантирует, что при наличии более 1 вы будете запускать их только на разных данных. Примерно так:

procedure my_procedure ( PNumerator number, PDenominator number ) is

    cursor c_my_cursor ( CNumerator number, CDenominator number ) is
     select columns
       from my_table
      where mod( ascii(substr(id, -1)), CDenominator ) = CNumerator
            ;

begin
   open c_my_cursor( PNumerator, PDenominator );
   ...    
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...