【问题标题】:Oracle 12c: Parallel SELECT FOR UPDATE NOWAIT on disjoint sets of rows causes ORA-00054: resource busyOracle 12c:在不相交的行集上并行 SELECT FOR UPDATE NOWAIT 导致 ORA-00054:资源繁忙
【发布时间】:2017-08-07 05:41:04
【问题描述】:

我在几个并发进程中运行以下查询:

SELECT A_ID, B_ID, C_ID, C_STATUS, D_ID
FROM A NATURAL JOIN B NATURAL JOIN C 
WHERE A_ID IN (...)
FOR UPDATE OF C_STATUS, D_ID NOWAIT;
  • 表 A:A_ID (PK)
  • 表 B:B_ID (PK)、A_ID (FK)
  • 表 C:C_ID (PK)、C_STATUS、B_ID (FK)、D_ID (FK)
  • 表 D:D_ID (PK)

每个进程在IN (...) 列表中都有自己的一组值,并且这些组保证是不相交的。

不确定这是否重要,但表 D 的 FK 也是不相交的 - 更新之前和之后都会稍后完成。

不过,我偶尔会收到 ORA-00054: resource busy,我将其解读为“两个进程试图在 NOWAIT 语句中锁定同一行以进行更新”。

在我使用 FOR UPDATE OF C_STATUS, D_ID NOWAIT 子句之前,并行查询正在等待其他查询完成(等待释放锁),并且在尝试更新表 C 的相应行时,我很少遇到死锁:

死锁图:

                        ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name           process session holds waits  process session holds waits
TX-000F001F-0000F3B5-.. 39    1414     X             75     835           S
TX-0009000B-000124A5-.. 75     835     X             39    1414           S

锁定的行来自表 C。但是,在检查阻塞行的调试日志和 rowid 时,我发现至少有一个进程不应该接触该行。

知道为什么我在多个进程更新不相交的行时会出现资源繁忙/死锁吗?为什么 Oracle 会锁定实际上没有被使用的行??

编辑:我能够将问题缩小到这个 bash 脚本:

#!/bin/bash
sub(){
sqlplus -S "$DB_ACCESS" << EOF
exec dbms_lock.sleep($2);
select '$1:'||C_ID from C where C_ID in ($3)
for update nowait;
exec dbms_lock.sleep(2);
rollback;
EOF
}
sub 1 0.1 1510223
sub 2 0.3 1510600
sub 3 0.5 1512100
wait

您可以看到 C_ID 不同,我检查了这三个的父 B_ID 和祖父母 A_ID 也不同。

我得到以下输出:

PL/SQL procedure successfully completed.
'1:'||C_ID
------------------------------------------
1:1510223
PL/SQL procedure successfully completed.
Rollback complete.
PL/SQL procedure successfully completed.
'2:'||C_ID
------------------------------------------
2:1510600
PL/SQL procedure successfully completed.
Rollback complete.
PL/SQL procedure successfully completed.
select '3:'||C_ID from C where C_ID in (1512100)
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
PL/SQL procedure successfully completed.
Rollback complete.

我希望我可以提供示例数据,但我只能在将 ~1000000 行加载到表 C 中后才能重现此问题。

【问题讨论】:

  • 是否所有*_ID 列都被索引了?
  • 是的,所有的PK和FK都被索引了。
  • 如果我没看错,“在我有 FOR UPDATE OF C_STATUS, D_ID NOWAIT 子句之前,并行查询正在等待其他完成”是不正确的。在您添加 FOR UPDATE 之前,查询可以同时运行,因为 Oracle 不会在读取时锁定记录。但是,现在,使用此子句,您将在每次读取时锁定行,因此,第一个会话将获取锁,而第二个会话如果在第一个会话仍在运行时运行,将收到有关已锁定行的错误。
  • @blaf 你能用一些示例表/数据生成这个吗?
  • @unleashed:查询是同时运行的,但是正如我上面提到的,“稍后在尝试更新相应行的过程中”,就会发生死锁。

标签: sql oracle oracle12c database-deadlocks


【解决方案1】:

好的,我找到了根本原因。这是一个 ITL 锁定和等待。

更多信息在这里:ITL waits demystified

诀窍在于三行都是同一物理块的一部分,几乎已满,并且这些行的锁也存储在物理块中。第三个锁没有足够的空间,因此第三个事务将等待。

【讨论】:

  • 以前我没有设置 INITRANS,只存储了两个 ITL。我把它增加到 32 并且很好。
猜你喜欢
  • 2016-03-28
  • 2011-03-05
  • 2011-07-02
  • 2011-06-18
  • 2012-10-09
  • 2018-03-16
  • 2013-10-04
  • 1970-01-01
相关资源
最近更新 更多