有没有最好的方法可以避免在Oracle中多次执行进程?

时间:2012-03-22 15:07:29

标签: oracle plsql

假设我有procedure名为myproc。这是一个复杂的过程,我不能允许两个实例同时执行proc。

实际上我使用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 个答案:

答案 0 :(得分:10)

使用dbms_lock.allocate_uniquedbms_lock.request使用说明说:

  

使用新锁名称调用ALLOCATE_UNIQUE的第一个会话导致   要生成并存储在dbms_lock_allocated表中的唯一锁ID。   后续调用(通常由其他会话)返回先前生成的锁ID。

我认为这可能就是你所追求的目标。

答案 1 :(得分:1)

您可以创建表格processes。您还要确保每个进程都有某种唯一标识符 - 例如来自dba_objectsowner, object_name的哈希值,以便您可以在包中动态创建它。

然后在运行流程时创建一个lock each row individually函数。

正如@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;

只要每个程序都有一个唯一的ID - 重要的一点 - 您的程序就会彻底退出。


它可能不适用于您的情况但是,我这样做的正常方法是使用mod。虽然它不会停止运行相同进程的2个,但确保当您有多个进程时,只能在不同的数据上运行它们。如下所示:

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;