记一次Oracle ORA-01013和DBMS_STATS: ORA-04021出现的问题解决

文章讲述了在Oracle数据库中遇到的大表数据插入失败问题,通过排查发现是由于表被锁,且与DBMS_STATS的自动统计任务和SQLTuningAdvisor有关。通过禁用SQLTuningAdvisor解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.问题出现

因业务需要,数据库中有一张表每时每刻都有数据插入,数据量比较庞大,都是些物联网数据。最近一段时间,发现在工作日周一至周五晚上10点多,和周六周日早上6点多发生数据插入失败的情况,经日志记录,在insert那张表时Oracle数据库报错:ORA-01013: 用户请求取消当前的操作

2.问题排查

1)排除ORM框架问题

一开始是怀疑自己的ORM框架存在问题,由于我用的是C#语言,采用了SqlSugar作为ORM框架。后替换为Oracle原生框架也存在insert失败或一直等待的情况,所以排除了ORM框架问题。

2)排除SQL语句执行方式问题

不管用事务方式批量插入数据还是直接SQL语句单条插入,均存在ORA-01013问题。

3)排除操作系统问题

在问题发生时刻,在另一台计算机上执行同样的SQL语句一样报错。

3.问题定位

经测试,问题发生时,仅对那张大数据表的插入产生了报错,而别的表的插入查询均未出现问题。初步判定为那张表被锁。在数据库服务器上翻阅trace日志,发现问题产生时刻存在如下trc文件记录:

*** 2023-09-23 06:36:58.786
*** SESSION ID:(6333.24337) 2023-09-23 06:36:58.786
*** CLIENT ID:() 2023-09-23 06:36:58.786
*** SERVICE NAME:(SYS$USERS) 2023-09-23 06:36:58.786
*** MODULE NAME:(DBMS_SCHEDULER) 2023-09-23 06:36:58.786
*** ACTION NAME:(ORA$AT_OS_OPT_SY_19107) 2023-09-23 06:36:58.786
 
ORA-04021: timeout occurred while waiting to lock object 

*** 2023-09-23 06:36:58.786
DBMS_STATS: GATHER_STATS_JOB: GATHER_TABLE_STATS('"XXX"','"XXXX"','""', ...)
DBMS_STATS: ORA-04021: timeout occurred while waiting to lock object 

*** 2023-09-23 06:52:13.068
DBMS_STATS: GATHER_STATS_JOB: GATHER_TABLE_STATS('"XXX"','"XXXXX"','"XXXX"', ...)
DBMS_STATS: ORA-04021: timeout occurred while waiting to lock object 

*** 2023-09-23 07:07:30.528
DBMS_STATS: GATHER_STATS_JOB: GATHER_TABLE_STATS('"XXX"','"XXXXX"','"XXXX"', ...)
DBMS_STATS: ORA-04021: timeout occurred while waiting to lock object 

再登录数据库SYS账号,查询发生问题时刻执行的SQL语句,找到了:

SELECT SUM(x) FROM  (SELECT CASE WHEN B != LAG(B) OVER (ORDER BY "XXX") THEN 1 ELSE 0 END x   FROM    (SELECT "XXX", DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) b     FROM "XX"."XXXX"     WHERE ROWNUM < 1000))

正是需要插入的那张表,执行者为SYS,所以排除业务数据库XXX的定时任务等操作。

由于时间发生比较特殊,为工作日晚10点多和周末早6点多,顺着这个思路继续探索。

查询SYS的计划任务:

select window_name,repeat_interval,duration,enabled from dba_scheduler_windows;

结果:

发现了工作日和周末的执行时间。那执行的是什么,经过度娘的帮助,发现是Oracle的自动任务可能会产生ORA-04021: timeout occurred while waiting to lock object 问题。

查询自动维护任务:

select client_name,status from dba_autotask_client;

有三个任务:auto optimizer stats collection、auto space advisor、sql tuning advisor。其中第三个好像是SQL优化建议的,尝试把第三个任务关闭:

--关闭sql tuning advisor
BEGIN
  DBMS_AUTO_TASK_ADMIN.disable(
    client_name => 'sql tuning advisor',
    operation   => NULL,
    window_name => NULL);
END;

至此,问题解决!!!

<think>好的,我现在需要帮助用户解决他们遇到的cx_Oracle.DatabaseError: ORA-08103错误。首先,我得理解这个错误的原因。ORA-08103表示用户尝试访问的对象在数据库中已经不存在了。这可能发生在查询过程中,对象被其他会话删除或修改,导致当前会话无法访问。 接下来,我需要分析用户提供的代码片段`results = cursor.fetchall()`。用户在执行这个语句时触发了错误,说明问题出在查询的对象上。可能的情况包括表或视图被删除、对象名称拼写错误,或者存在并发操作导致对象结构变化。 首先,我会考虑对象是否被删除或重命名。用户可能在执行查询之前,另一个会话执行了DDL操作,比如DROP或ALTER,导致对象不存在。这时候需要检查数据库日志,确认是否有相关的DDL操作发生。 然后,检查对象名称是否正确。用户可能在代码中写错了表名或视图名,或者在模式(schema)中没有正确指定,比如没有使用模式名前缀。需要仔细核对SQL语句中的对象名称,确保大小写拼写正确,特别是在Oracle中,对象名默认是大写的,除非使用了双引号。 另外,考虑并发操作的影响。如果对象在查询期间被修改,比如使用闪回操作或者在线重定义,可能会导致对象版本不一致。这时候可能需要调整事务隔离级别或者使用适当的锁机制来避免这种情况。 权限问题也不能忽视。用户是否有访问该对象的权限?可能需要检查数据库中的权限设置,确认用户是否有SELECT权限或者其他必要的权限。 针对解决方法,首先建议用户立即验证对象的当前状态,查询数据字典视图如USER_OBJECTS或ALL_OBJECTS,确认对象是否存在。如果对象确实被删除,需要恢复或重建。如果是权限问题,需要授予相应的权限。 另外,检查代码中的事务管理。如果使用了未提交的事务,可能会导致对象被锁定或修改,需要确保事务正确提交或回滚,避免长时间持有锁。 最后,建议用户处理异常,添加重试机制,特别是对于可能由于并发操作导致的临时错误,可以捕获异常后等待一段时间再重试,增加程序的健壮性。 需要将这些分析整理成用户易懂的步骤,帮助他们逐步排查问题,确保回答结构清晰,解决方案具体可行。同时注意使用中文,保持口语化,避免技术术语过于复杂,让用户能够轻松理解操作。</think>你的错误涉及Oracle数据库ORA-08103错误,以下是逐步排查与解决方法: ----- ### 错误含义 `ORA-08103: object no longer exists` 表示你尝试访问的**数据库对象(如表、视图、索引等)在查询过程中被删除或失效**。常见于高并发场景或DDL操作干扰。 ----- ### 原因分析 1. **并发DDL操作** - 其他会话在查询期间执行了`DROP TABLE`或`ALTER TABLE ... DROP COLUMN`等操作 - 例如:A会话正在查询表`employees`时,B会话突然删除了该表 2. **闪回操作干扰** - 使用`FLASHBACK TABLE`恢复表时,可能导致对象状态异常 3. **对象名称错误** - 代码中引用了不存在/拼写错误的对象名(注意Oracle默认对象名大写) - 例如:`SELECT * FROM Emplyees`(正确表名为`EMPLOYEES`) 4. **临时表异常** - 使用`GLOBAL TEMPORARY TABLE`时,事务未正确处理导致表结构失效 ----- ### 诊断步骤 1. **验证对象状态** 执行以下SQL检查目标对象是否存在: ```sql SELECT object_name, object_type, status FROM all_objects WHERE object_name = UPPER('your_table_name'); ``` 2. **检查DDL日志** 查询`DBA_OBJECTS`的`LAST_DDL_TIME`: ```sql SELECT owner, object_name, object_type, last_ddl_time FROM dba_objects WHERE object_name = UPPER('your_table_name'); ``` 3. **会话追踪** 通过`v$session`检查是否有其他会话持有DDL锁: ```sql SELECT sid, serial#, sql_id, sql_exec_start FROM v$session WHERE sql_text LIKE '%DROP%' OR sql_text LIKE '%ALTER%'; ``` ----- ### 解决方案 1. **对象恢复** - 如果表被删除,立即停止相关操作 - 从备份恢复或使用闪回(需开启回收站): ```sql FLASHBACK TABLE your_table TO BEFORE DROP; ``` 2. **代码修正** - 添加对象存在性检查: ```python cursor.execute(""" SELECT COUNT(*) FROM all_objects WHERE object_name = UPPER('your_table') AND owner = UPPER('your_schema') """) exists = cursor.fetchone()[0] if not exists: raise Exception("目标表不存在") ``` 3. **事务隔离** 使用`SELECT ... FOR UPDATE NOWAIT`锁定资源: ```python cursor.execute("SELECT * FROM your_table FOR UPDATE NOWAIT") ``` 4. **异常重试机制** ```python import time from cx_Oracle import DatabaseError max_retries = 3 for attempt in range(max_retries): try: cursor.execute("SELECT ...") results = cursor.fetchall() break except DatabaseError as e: if 'ORA-08103' in str(e) and attempt < max_retries-1: print(f"对象状态异常,第{attempt+1}次重试...") time.sleep(2 ** attempt) # 指数退避 continue else: raise ``` ----- ### 预防措施 1. **开发规范** - DDL操作需在维护窗口执行 - 生产环境禁止直接执行`DROP TABLE`,改用重命名: ```sql ALTER TABLE your_table RENAME TO your_table_archived; ``` 2. **监控配置** ```sql -- 创建DDL触发器录操作日志 CREATE OR REPLACE TRIGGER audit_ddl AFTER DDL ON DATABASE BEGIN INSERT INTO ddl_audit_log VALUES (sysdate, ora_sysevent, ora_dict_obj_name); END; ``` 3. **对象版本控制** ```sql -- 查询对象依赖关系 SELECT * FROM all_dependencies WHERE referenced_name = 'YOUR_TABLE'; ``` 建议结合Oracle的`DBMS_LOCK`包实现更细粒度的锁控制,同时定期使用`DBMS_STATS`收集统计信息保持对象状态稳定。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值