以下代码会导致死锁吗?或者它应该可以正常工作吗?我有类似的东西并且它正在工作,但我认为不会。我认为父过程的锁会导致子过程的死锁,但事实似乎并非如此。
如果有效,为什么?我的猜测是,嵌套的 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/