触发器(八、用SERVERERROR触发器开展数据库错误维护工作)
SERVERERROR 触发器是一种数据库(database)级别的触发器,在Oracle错误发生后触发,但是不包含以下ORA错误:
ORA-00600 Oracle internal error
ORA-01034 Oracle not available
ORA-01422 Exact fetch returns more than requested number of rows
ORA-01423 Error encountered while checking for extra rows in an exact fetch
ORA-04030 Out-of-process memory when trying to allocate N bytes
作为DBA需要时时监控并及时处理Oracle数据库发生的错误,用SERVERERROR触发器可以快速捕捉到错误,实施过程如下:
1.创建错误日志表
SQL> DESC t_log
Name Type Nullable Default Comments
------ -------------- -------- ------- --------
SPNAME VARCHAR2(60) Y
SDT DATE Y
EDT DATE Y
ERRMSG VARCHAR2(4000) Y
CLBZ VARCHAR2(1) Y
2.创建SERVERERROR触发器
记录发生错误的客户端信息以及错误信息,每发生一次记录一条
CREATE OR REPLACE TRIGGER TRI_SERVERERROR
AFTER SERVERERROR
ON DATABASE
DECLARE
V_ERRTXT VARCHAR2(32767);
BEGIN
V_ERRTXT:=USER||CHR(10);
V_ERRTXT:=V_ERRTXT||'host:'||SYS_CONTEXT('USERENV','HOST')||chr(10);
V_ERRTXT:=V_ERRTXT||'ip:'||SYS_CONTEXT('USERENV','IP_ADDRESS')||CHR(10);
FOR I IN 1..ora_server_error_depth LOOP
V_ERRTXT:=V_ERRTXT||ora_server_error_msg(I)||CHR(10);
END LOOP;
MERGE INTO t_log A
USING (SELECT 'SERVERERROR' SPNAME,SYSDATE EDT,V_ERRTXT ERRMSG ,'N' CLBZ FROM DUAL) B
ON (A.SPNAME=B.SPNAME AND A.ERRMSG=B.ERRMSG)
WHEN MATCHED THEN
UPDATE SET A.EDT=B.EDT,A.CNT=NVL(A.CNT,0)+1,A.CLBZ=B.CLBZ
WHEN NOT MATCHED THEN
INSERT (SPNAME,SDT,EDT,ERRMSG,CLBZ,CNT)
VALUES(B.SPNAME,SYSDATE,NULL,B.ERRMSG,B.CLBZ,1);
END;
/
1天以后,我惊讶的发现已经记录了1.5万+的错误信息,其中ORA-25228发生了1.4万多。
这个错误应该是OG*生的,如果复制队列的queue是空的就会报这个错,网上有一篇stream复制产生同样错误的文章
http://www.dba-oracle.com/t_ora_25228.htm
3.完善
这个错误显然是可以忽略的,但我还是决定把所有错误都记录下来,所以我采取了一种不丢弃数据的方法,在日志表里增加了数量字段:
SQL> ALTER TABLE YHGL.t_log ADD CNT NUMBER(10);
Table altered.
SQL> DESC t_log
Name Type Nullable Default Comments
------ -------------- -------- ------- --------
SPNAME VARCHAR2(60) Y
SDT DATE Y
EDT DATE Y
ERRMSG VARCHAR2(4000) Y
CLBZ VARCHAR2(1) Y
CNT NUMBER(10) Y
接着对触发器进行了修改,用merge代替insert,如果是同一个错误,更新数量cnt和最后发生的时间edt
CREATE OR REPLACE TRIGGER TRI_SERVERERROR
AFTER SERVERERROR
ON DATABASE
DECLARE
V_ERRTXT VARCHAR2(32767);
BEGIN
V_ERRTXT:=USER||CHR(10);
V_ERRTXT:=V_ERRTXT||'host:'||SYS_CONTEXT('USERENV','HOST')||chr(10);
V_ERRTXT:=V_ERRTXT||'ip:'||SYS_CONTEXT('USERENV','IP_ADDRESS')||CHR(10);
FOR I IN 1..ora_server_error_depth LOOP
V_ERRTXT:=V_ERRTXT||ora_server_error_msg(I)||CHR(10);
END LOOP;
MERGE INTO t_log A
USING (SELECT 'SERVERERROR' SPNAME,SYSDATE EDT,V_ERRTXT ERRMSG ,'N' CLBZ FROM DUAL) B
ON (A.SPNAME=B.SPNAME AND A.ERRMSG=B.ERRMSG)
WHEN MATCHED THEN
UPDATE SET A.EDT=B.EDT,A.CNT=NVL(A.CNT,0)+1,A.CLBZ=B.CLBZ
WHEN NOT MATCHED THEN
INSERT (SPNAME,SDT,EDT,ERRMSG,CLBZ,CNT)
VALUES(B.SPNAME,SYSDATE,NULL,B.ERRMSG,B.CLBZ,1);
END;
/
再跟踪一下日志情况,变成了这样:
4.后续
现在的日志已经 基本上满足我日常管理的需要了。
接下去就做了个前台页面,做2个事情:
1.对未处理的错误进行分析、确认、处理;
2.对无关紧要的错误做白名单标记,只记录,无需处理。