子过程过程是否可以锁定并修改其调用过程已锁定的相同行FOR UPDATE?

时间:2010-05-23 03:03:34

标签: sql oracle plsql deadlock

以下代码会导致死锁还是应该没有任何问题?我有类似的东西,它的工作,但我认为它不会。我认为父进程的锁会导致子进程的死锁,但似乎不是。

如果有效,为什么?我的猜测是嵌套的FOR UPDATE没有遇到死锁,因为它足够聪明地意识到它是由具有当前锁的相同过程调用的。

如果FOO_PROC不是嵌套程序,这会是一个死锁吗?

DECLARE
  FOO_PROC(c_someName VARCHAR2) as
    cursor c1 is select * from awesome_people where person_name = c_someName FOR UPDATE;
  BEGIN
    open c1;
    update awesome_people set person_name = UPPER(person_name);
    close c1;
  END FOO_PROC;

  cursor my_cur is select * from awesome_people where person_name = 'John Doe' FOR UPDATE;
BEGIN
  for onerow in c1 loop
    FOO_PROC(onerow.person_name);
  end loop;
END;

1 个答案:

答案 0 :(得分:3)

它不会导致死锁。这只能在两个会话更新同一行时发生,因为它们采用乐观锁定策略。这是发生的事情

一些测试数据:

 SQL> select * from t23
   2  /

 PERSON_NAME
 -----------------------------------------------------------------------------
 Fox in socks
 Mr Knox
 Sam-I-Am
 The Lorax
 John Doe

 SQL>

这是你的匿名(使用更正的sybtax):.

 SQL> declare
   2      cursor c_jd is
   3          select *
   4          from t23
   5          where person_name = 'John Doe'
   6          for update of person_name;
   7      procedure foo_proc
   8          ( p_name in t23.person_name%type)
   9      is
  10          cursor c_fp is
  11              select *
  12              from t23
  13              where person_name = p_name
  14              for update of person_name;
  15          r_fp c_fp%rowtype;
  16      begin
  17          open c_fp;
  18          fetch c_fp into r_fp;
  19          update t23
  20          set person_name = upper(r_fp.person_name)
  21          where current of c_fp;
  22          close c_fp;
  23      end foo_proc;
  24  begin
  25      for onerow in c_jd loop
  26          foo_proc(onerow.person_name);
  27      end loop;
  28  end;
  29  /

 PL/SQL procedure successfully completed.

 SQL>

这就是结果

SQL>从t23中选择*        2 /

 PERSON_NAME
 -----------------------------------------------------------------------------
 Fox in socks
 Mr Knox
 Sam-I-Am
 The Lorax
 JOHN DOE

SQL>

它成功了吗?因为FOR UPDATE是会话级锁定。这两个锁是从同一个会话发出的,因此Oracle足够智能,可以在不发生争用的情况下解决它们。如果你要在FOO_PROC()声明一个PRAGMA AUTONOMOUS_TRANSACTION之类的东西,它会投掷

ORA-00060: deadlock detected while waiting for resource

在同一会话中两次调用FOR UPDATE并不会以这种方式失败的事实是一个重要的架构设计。在不查看源代码的情况下,无法判断过程是否发出锁定。因此,当PROC_A()调用PROC_B()时,它不知道该过程是否发出锁定。但PROC_A()可以发出自己的锁,确信此操作不会导致PROC_B()失败。这是一件好事,因为它坚持Demeter法并减少耦合。

当然,你的场景是人为的,在代码审查中会被拒绝,但这是一个不同的问题!

修改

  

“为了测试这个,我做了FOO_PROC   自治,它没有碰到一个   僵局;是因为它在   同一个会议?“

你确定吗? AUTONOMOUS_TRANSACTION pragma恰恰意味着FOO_PROC()在其自己的离散会话中运行,因此无法获得锁定:

SQL> declare
  2      cursor c_jd is
  3          select *
  4          from t23
  5          for update of person_name;
  6      procedure foo_proc
  7          ( p_name in t23.person_name%type)
  8      is
  9          pragma autonomous_transaction;
 10          cursor c_fp is
 11              select *
 12              from t23
 13              where person_name = p_name
 14              for update of person_name;
 15          r_fp c_fp%rowtype;
 16      begin
 17          dbms_output.put_line('Inside FP');
 18          open c_fp;
 19          fetch c_fp into r_fp;
 20          update t23
 21          set person_name = upper(r_fp.person_name)
 22          where current of c_fp;
 23          close c_fp;
 24          commit;
 25      end foo_proc;
 26  begin
 27      for onerow in c_jd loop
 28          dbms_output.put_line('Outer loop START');
 29          foo_proc(onerow.person_name);
 30          dbms_output.put_line('Outer loop END');
 31      end loop;
 32  end;
 33  /
Outer loop START
Inside FP
declare
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
ORA-06512: at line 11
ORA-06512: at line 18
ORA-06512: at line 29


SQL>

(我添加了一些DBMS_OUTPUT语句来显示正在发生的事情)。

  

“当你说代码示例时我   提供的是不好的做法,你是什么   意思?“

我的意思是让一个循环驱动SELECT语句调用另一个从同一个表中选择的程序。实际上,它选择了同一行。一般来说,我们应该避免做不必要的工作。你已经有了这一行:为什么要再读一遍?