检测存储过程中的死锁

时间:2017-08-23 02:44:40

标签: oracle plsql

我正在尝试编写一个死锁证明存储过程。它基于以下概念。

我一直在尝试编写一个基于以下概念的存储过程。该过程将尝试在表上删除约束,如果它检测到死锁情况,则等待一段时间再重试。重要的是它只应该在死锁或NOWAIT错误的情况下重试,所有其他错误都要通过异常处理。

Procedure test
is

BEGIN
<<label>>

DROP constraint on a table

if (deadlock(ORA-00060)/Nowait Error (ORA-0054)) detected
then
sleep for 60 seconds

Goto label

exception
when others.

如果有任何专家请帮我解决这个问题会很棒。类似的例子非常有帮助。谢谢你的帮助。

2 个答案:

答案 0 :(得分:3)

虽然有些人对goto抱有非理性的厌恶,但通常我们可以在不使用构造的情况下实现相同的逻辑。这是真的:只需要一个简单的while循环:

create or replace procedure drop_constraint_for_sure
    ( p_table_name in varchar2
      , p_constraint_name in varchar2
    )
is
    x_deadlock exception;
    pragma exception_init(x_deadlock, -60);
    x_nowait exception;
    pragma exception_init(x_nowait, -54);
begin
    loop
        begin
            execute immediate 'alter table '|| p_table_name 
                         || ' drop constraint ' || p_constraint_name
                         || ' cascade' ;
            exit;
        exception
            when x_deadlock then null;
            when x_nowait then null;
        end;
        dbms_lock.sleep(60);
    end loop;
end;
/

请注意,sleep函数需要SYS.DBMS_LOCK的execute权限。这是默认情况下未授予,因此如果您没有,则需要请求您的友好DBA授予它。

另请注意,此实现不具有任何形式的中止。因此它将永远循环,直到约束被删除或发生其他一些异常。在现实生活中,您应该包括一个循环计数,并在计数阈值上进行额外的退出测试。虽然在现实生活中我可能不会想要这样的存储过程:我更喜欢尽快知道有人在使用桌子时我试图改变。

答案 1 :(得分:1)

希望下面的代码段可以让您了解实现您的要求。

CREATE OR REPLACE
PROCEDURE DRP_CONST_DEALDOCK
AS
  DEADLOCK_EX EXCEPTION;
  NO_WAIT_EX  EXCEPTION;
  PRAGMA EXCEPTION_INIT(DEADLOCK_EX,-60);
  PRAGMA EXCEPTION_INIT(NO_WAIT_EX, -54);
  lv_cnt PLS_INTEGER;
BEGIN
  BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE STACK_OF_TEST DROP CONSTRAINT SYS_C00375020';
  EXCEPTION
  WHEN DEADLOCK_EX THEN
    dbms_output.put_line('nowait exception');
    DBMS_LOCK.SLEEP(10);
    SELECT COUNT(1)
    INTO lv_cnt
    FROM v$locked_object
    WHERE object_id IN
      (SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
      );
    WHILE lv_cnt > 0
    LOOP
      dbms_lock.sleep(10);
      SELECT COUNT(1)
      INTO lv_cnt
      FROM v$locked_object
      WHERE object_id IN
        (SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
        );
    END LOOP;
  WHEN NO_WAIT_EX THEN
    dbms_output.put_line('nowait exception');
    DBMS_LOCK.SLEEP(10);
    SELECT COUNT(1)
    INTO lv_cnt
    FROM v$locked_object
    WHERE object_id IN
      (SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
      );
    WHILE lv_cnt > 0
    LOOP
      dbms_lock.sleep(10);
      SELECT COUNT(1)
      INTO lv_cnt
      FROM v$locked_object
      WHERE object_id IN
        (SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'STACK_OF_TEST'
        );
    END LOOP;
  WHEN OTHERS THEN
    dbms_output.put_line(SQLCODE||'-->'||SQLERRM);
  END;
END;