为啥我的语句因资源繁忙异常而失败?

Posted

技术标签:

【中文标题】为啥我的语句因资源繁忙异常而失败?【英文标题】:Why my statement failed with resource busy exception?为什么我的语句因资源繁忙异常而失败? 【发布时间】:2019-11-29 13:05:12 【问题描述】:

作为 ETL 过程的一部分,我有这个交换子分区声明:

ALTER TABLE DWH.QV_FACT_AMS EXCHANGE SUBPARTITION P08_2018_300_SALES WITH TABLE DWH.STG_QV_FACT_AMS;

(每个运行不同的子分区但相同的 2 个表)。

我们开始遇到异常:

ORA-00054: 资源繁忙并获取指定的 NOWAIT 或超时 过期了。

这意味着某些进程更新了表,我们无法更改。

由于最终表 - QV_FACT_AMS 仅用于报告,而 STG_QV_FACT_AMS 是在 ETL 本身中创建的内部动态表,我不知道谁可以更新这些表。

ETL 不能与 ETL 本身的另一次运行发生冲突,因此不能尝试从这 2 个进程修改同一个表。

exchange 语句每天运行很多次并且正常完成,并且仅在晚上 9 点左右引发异常。

所以我对这 2 个表应用了 AUDIT ALL:

AUDIT ALL on dwh.qv_Fact_ams;
AUDIT ALL on dwh.stg_qv_Fact_ams;

但是对表格的所有访问都不是在那个时间段,它只是 select 不应该锁定表格。

失败日志示例:

LOG_ID: 5879089
START_TIME: 18-07-2019 21:29:01
END_TIME: 18-07-2019 21:29:28
STATUS: FAILED
ORA_EXCEPTION: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

我们可以看到异常发生在 18-07-2019 21:29:28。

这是审计:

SELECT  * 
FROM DBA_MAINT.AUD$ 
WHERE OBJ$NAME IN ('QV_FACT_AMS','STG_QV_FACT_AMS') 
ORDER BY TIMESTAMP# DESC;

除了etl本身之外,唯一的访问是只有action# = 3(选择),3小时后......

【问题讨论】:

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired的可能重复 如何重复?你发送的问题是关于这个异常的原因。我知道有东西锁了桌子。我只是找不到锁定会话。 【参考方案1】:

在会话级别设置 DDL_LOCK_TIMEOUT 以找出谁在阻止命令。

使用默认设置,锁定表上的 DDL 将立即失败:

--Session #1: Insert but do not commit:
create table table1(a number);
insert into table1 values(1);

--Session #2:
--ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
alter table table1 move;

相反,我们可以告诉 Oracle 等待一定的秒数,而不是立即失败。

--Session #2:
--The second command will hang for 9999 seconds, or until the lock is free.
alter session set ddl_lock_timeout = 9999;
alter table table1 move;

这给了我们足够的时间来调查是什么会话和语句阻塞了我们的命令。找到拦截器的方法有很多,可能会有很多误报。这段代码是一个很好的起点:

--Find potential blockers:
select sid, final_blocking_session, gv$session.*
from gv$session
where final_blocking_session is not null;

一旦我们有了 FINAL_BLOCKING_SESSION(这是阻塞会话的 SID),我们就可以找到有关该会话的更多信息,例如它正在运行的语句。此 SQL 语句可以帮助调查拦截器:

--Use the FINAL_BLOCKING_SESSION from above.
select *
from gv$sql
join gv$session
    on gv$sql.sql_id = gv$session.sql_id
where sid = <final_blocking_session>;

希望这些信息中的一些有意义并揭示罪魁祸首。由于更改没有显示在审计跟踪中,我猜有一个 SYS 用户正在运行一个命令,因为 SYS 没有在同一个地方审计。

【讨论】:

以上是关于为啥我的语句因资源繁忙异常而失败?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的查询因 SQLCODE = -420 而失败?

为啥我的单元测试因错误“str”对象没有属性“光标”而失败?

为啥我的 psycopg2 查询因 `psycopg2.errors.InternalError_: Assert` 而失败?

为啥 sed 会因国际字符而失败以及如何解决?

为啥我的产品->存档因“非法尝试在不同上下文中的对象之间建立关系”压缩类型“而失败?

为啥 adb 因失败 [INSTALL_PARSE_FAILED_NO_CERTIFICATES] 而失败?