最近,遇到一個很奇怪的問題。一Oracle數據庫的兩個存儲過程,單獨跑不會有問題,但是同時跑其中一個存儲過程
就會遇到ORA08103報錯。
一、問題報錯信息如下:
報錯ORA-08103: 對象不再存在,ORA-06512: 在 "REPD.ETL_PPACKAGE", line 7901
--根據報錯提示,報錯涉及的SQL語句是一個Insert into ...... select
V_SQL2:= ' INSERT INTO DATA_T (
C1 ,
C2 ,
C3
)
SELECT
b.T1,
b.T2,
b.T3,
FROM data_b;
二、問題分析
1、第一印象,可能是跨用戶數據訪問,表被宿主刪掉或者報錯表上發生了truncate操作。於是,使用ddl審計監控數據
庫發生的DDL操作。
--首先sys確認觸發器相關的參數
show parameter _system_trig_enabled
--如果_system_trig_enabled參數值爲false,修改爲true
alter system set "_system_trig_enabled"=true;
--確認觸發器相關的參數_system_trig_enabled參數值爲true
show parameter _system_trig_enabled
--SYS登陸數據庫創建審計表
-- Create table
create table AUDIT_DDL_OBJ
(
opr_time DATE,
session_id NUMBER,
os_user VARCHAR2(200),
ip_address VARCHAR2(200),
terminal VARCHAR2(200),
host VARCHAR2(200),
user_name VARCHAR2(30),
ddl_type VARCHAR2(30),
ddl_sql CLOB,
object_type VARCHAR2(18),
owner VARCHAR2(30),
object_name VARCHAR2(128)
)
tablespace USERS
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
next 1M
minextents 1
maxextents unlimited
);
--創建DDL審計觸發器
CREATE OR REPLACE TRIGGER DDL_Audit_Trigger
AFTER drop or alter or truncate ON DATABASE
/*
||名稱:DDL事件審計觸發器
||說明:
*/
DECLARE
Session_Id_Var NUMBER; /* 會話ID */
Os_User_Var VARCHAR2(200); /* 終端OS用戶 */
IP_Address_Var VARCHAR2(200); /* 終端IP */
Terminal_Var VARCHAR2(200); /* 終端 */
Host_Var VARCHAR2(200); /* 終端主機名 */
Cut NUMBER; /* SQL列表長度 */
Sql_Text ORA_NAME_LIST_T; /* SQL_TEXT 列表 */
L_Trace NUMBER; /* 循環執行條件 */
DDL_Sql_Var VARCHAR2(2000); /* DDL語句 */
stmt clob := NULL;
n NUMBER;
BEGIN
/* 獲取操作用戶信息 */
SELECT SYS_CONTEXT('USERENV','SESSIONID'),
SYS_CONTEXT('USERENV','OS_USER'),
SYS_CONTEXT('USERENV','IP_ADDRESS'),
SYS_CONTEXT('USERENV','TERMINAL'),
SYS_CONTEXT('USERENV','HOST')
INTO Session_Id_Var,
Os_User_Var,
IP_Address_Var,
Terminal_Var,
Host_Var
FROM DUAL;
/* 獲取DDL SQL語句 */
/*BEGIN
SELECT sql_text
INTO DDL_Sql_Var
FROM sys.v_$sql a, sys.v_$session b
WHERE a.hash_value = b.sql_hash_value
AND b.status = 'active'
AND b.audsid = SYS_CONTEXT('userenv', 'sessionid');
EXCEPTION
WHEN OTHERS THEN
NULL;
END;*/
n := ora_sql_txt(sql_text);
FOR i IN 1 .. n LOOP
stmt := stmt || sql_text(i);
END LOOP;
/* 記錄登陸審計信息 */
INSERT INTO Audit_DDL_OBJ(
Opr_Time, /* 操作時間 */
Session_Id, /* 會話ID */
OS_User, /* 終端OS用戶 */
IP_Address, /* 終端IP地址 */
Terminal, /* 終端 */
Host, /* 終端主機名 */
User_Name, /* ORACLE 用戶名*/
DDL_Type, /* DDL操作類型 */
DDL_Sql, /* DDL語句 */
Object_Type, /* 操作對象類型 */
Owner, /* 對象擁有者 */
Object_Name /* 對象名稱 */
)
VALUES( SYSDATE,
Session_Id_Var,
Os_User_Var,
IP_Address_Var,
Terminal_Var,
Host_Var,
ORA_LOGIN_USER,
ORA_SYSEVENT,
stmt,
ORA_DICT_OBJ_TYPE,
ORA_DICT_OBJ_OWNER,
ORA_DICT_OBJ_NAME);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
NULL;
END DDL_Audit_Trigger;
========
觸發器後期清理
drop triger DDL_Audit_Trigger;
drop table AUDIT_DDL_OBJ;
alter system set "_system_trig_enabled"=false;
2、分析報錯的
根據DDL審計表AUDIT_DDL_OBJ信息,沒有發現DATA_T相關的DROP、TRUNCATE操作。
3、經與系統負責人溝通,採用10046跟蹤執行跑批報錯的會話
1 查詢要跟蹤的用戶會話信息
select sid,serial#,username from v$session where username='REPD';
2 開啓10046跟蹤
exec dbms_system.set_ev(sid,serial#,10046,12,'');
3 程序拋出錯誤後結束10046跟蹤
exec dbms_system.set_ev(sid,serial#,10046,0,'');
4 獲取10046跟蹤日誌文件,第1步中查詢到的sid號
select p.tracefile from V$PROCESS p,v$session s where s.paddr=p.addr and sid=&sid;
4、根據10046跟蹤文件獲取到ORA08103報錯對象的數據文件號和數據塊號
5、根據10046跟蹤文件ORA08103提示的數據文件號和數據塊號,確定到insert into ..... select相關表的一個索引對象
6、再次審查DDL審計觸發器,在存儲過程跑批之前,確實有該索引的drop操作
7、至此ORA08103問題根源定位到
導致ORA08103報錯的原因就是其中一個存儲過程在執行insert into ..... select操作過程中,另外一個存儲過程對insert
into ... select的select部分的表上的索引執行了drop,導致select查詢異常報錯ORA08103退出。
8、排查兩個同時執行報錯的存儲過程,其中一個存儲過程確實對審計表監控的索引執行了drop操作
三、問題處理
經與客戶溝通後,屏蔽存儲過程中drop索引的操作,再執行存儲過程,不再有ORA08103報錯。
四、補充
導致ORA08103報錯的可能原因有:
1、被操作的對象確實被其他用戶刪除掉
2、被操作的對象被執行了truncate操作
3、系統表空間數據塊損壞導致的對象字典信息與表所在表空間數據對象信息不一致
4、被操作對象發生了DDL例如加字段操作
5、被操作對象上的索引被刪除,操作使用到了對象上的索引