为啥我的语句因资源繁忙异常而失败?
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 没有在同一个地方审计。
【讨论】:
以上是关于为啥我的语句因资源繁忙异常而失败?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的单元测试因错误“str”对象没有属性“光标”而失败?
为啥我的 psycopg2 查询因 `psycopg2.errors.InternalError_: Assert` 而失败?