欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

触发器(八、用SERVERERROR触发器开展数据库错误维护工作)

程序员文章站 2022-07-13 14:11:39
...

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万多。
触发器(八、用SERVERERROR触发器开展数据库错误维护工作)

这个错误应该是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;
/

再跟踪一下日志情况,变成了这样:
触发器(八、用SERVERERROR触发器开展数据库错误维护工作)

4.后续

现在的日志已经 基本上满足我日常管理的需要了。
接下去就做了个前台页面,做2个事情:
1.对未处理的错误进行分析、确认、处理;
2.对无关紧要的错误做白名单标记,只记录,无需处理。