sql - 子过程过程是否可以锁定并修改其调用过程已锁定的相同行进行更新?

标签 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;

最佳答案

不会造成死锁。只有当两个 session 更新同一行时才会发生这种情况,因为它们采用乐观锁定策略。这是发生的事情

一些测试数据:

 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是 session 级别的锁。这两个锁是从同一个 session 发出的,因此 Oracle 足够聪明,可以在没有争用的情况下解决它们。然而,如果你要做一些类似在 FOO_PROC() 中声明 PRAGMA AUTONOMOUS_TRANSACTION 的事情,它会抛出

ORA-00060: deadlock detected while waiting for resource

在同一 session 中对 FOR UPDATE 的两次调用不会以这种方式失败,这一事实是架构设计的一个重要部分。如果不查看源代码,就不可能判断过程是否发出锁。因此,当 PROC_A() 调用 PROC_B() 时,它不知道该过程是否发出锁。但 PROC_A() 可以发出自己的锁,并确信此操作不会导致 PROC_B() 失败。这是一件好事,因为它维护了德米特定律并减少了耦合。

当然,您的场景是人为的,并且会在代码审查中被视为不好的做法而被拒绝,但这是一个不同的问题!

编辑

"To test this I did make FOO_PROC autonomous and it did not run into a deadlock; is that because it's in the same session?"

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

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 语句来显示发生的情况)。

"When you said the code example I provided was bad practice, what do you mean?"

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

关于sql - 子过程过程是否可以锁定并修改其调用过程已锁定的相同行进行更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2890550/

相关文章:

sql - MySQL MyISAM 表性能......痛苦地,痛苦地慢

php - 从 SQL 表中检索数据

Mysql查询查找指定行

sql - 当我返回 sql 列时,有没有办法只反转数字? (希伯来语)

oracle - PL/SQL Developer 中的转义符

oracle - oracle中动态调用过程

sql - 如何使用 ADO 和 VB 将 NULL 或空字符串传递给存储过程输入参数?

oracle - 如何在 TOAD 中传递数组参数

oracle - 带有变量帮助的 PLSQL select 语句

plsql - 外部命令,将外部变量返回给 Oracle Apex?