公司数据库因存储挂了,短时间无法恢复,需要通过读库、归档恢复到一个新的数据库。从而恢复业务
由于读库不全,从读库导过来的对象(表、sequence、procedure..)不全。致应用日志报“对象不存在”、数据库“library cache lock”、等等的大量错误及等待事件。
而开发也没法较快定位程序,不知道哪条sql报错(高并发环境下),所以暂不知道哪些对象缺失。
当时简短分析后,决定采用数据库触发器来捕获问题sql,触发类型为SERVERERROR。
最终的代码原型为:
--首先创建日志记录表,记录报错sql、主机等等一些信息
create table UTIL_ERROR_LOG
(
username VARCHAR2(50),
host VARCHAR2(50),
os_user VARCHAR2(50),
ip VARCHAR2(50),
type VARCHAR2(50),
time DATE default sysdate,
sql VARCHAR2(4000)
)
trigger --监控oracle错误号,这里的“对象不存在”对应的是ora-0942.
程序比较简单,但功能比较实用
create or replace trigger t_error_sql after SERVERERROR on database when (user not in('SYS') and ora_sysevent not in ('COMMENT')) declare n int; --n个的意思 sql_text ora_name_list_t; --v_sql clob; ORA_SQL_TXT读取到的sql含“/*中文*/”会乱码,clob装载这些乱码会报错 v_sql varchar(32767); --有可能读取到乱码 v_error varchar2(30); begin IF (IS_SERVERERROR(0942)) THEN v_error := '0942'; end if; if v_error is not null then n := ORA_SQL_TXT(sql_text); --sql_text(i)过了51就会报错,受不了这么大的值 if n < 51 then for i in 1 .. n loop --dbms_output.put_line(sql_text(i)); v_sql := v_sql || sql_text(i); end loop; end if; insert /*+append*/ into util_error_log values (user, sys_context('userenv', 'host'), sys_context('userenv', 'os_user'), sys_context('userenv', 'ip_address'), v_error, sysdate, v_sql); --也有补获不到sql的情况 end if; /*exception --调试信息 when others then dbms_output.put_line(n); raise_application_error(-20000, sqlerrm || ' ' || dbms_utility.format_error_backtrace() || ' N:' || n);*/ end t_error_sql; |
结:
Oracle trigger十分灵活,特别在生产上,不应随便创建。在经过严格的测试后,布置上去来实现一些事件前后的一些处理。
常用触发器实现的功能:
ddl语句监控
特殊核心表不能删保护
报错语句的监控
登陆




