oracle - "after servererror on database trigger"是个好主意吗?

标签 oracle performance oracle11g database-trigger audit

对于几个失败问题,我编写了这个触发器 -audit_failed_trg

作为数据库发生服务器错误后触发器。

我的第一个想法是根据需要仅检查特定的异常\用户\表。

但我只是想知道 - 数据库应该在发生任何故障时触发此触发器。

在生产环境中启用它是个好主意吗?

这是否会导致任何性能问题或其他问题?

我使用的是 Oracle 11g。

create or replace trigger audit_failed_trg
after servererror on database
declare
l_sql_text ora_name_list_t;
l_n        number;
  begin

insert into T values ( S.NEXTVAL, 1, 'ora_sysevent = ' || ORA_SYSEVENT ,sysdate);
insert into T values ( S.CURRVAL, 2, 'ora_login_user = ' || ORA_LOGIN_USER,sysdate );
insert into T values ( S.CURRVAL, 3, 'ora_server_error = ' || ORA_SERVER_ERROR(1),sysdate );
insert into T values ( S.CURRVAL, 4, 'SID = ' || SYS_CONTEXT ('USERENV','SID'),sysdate);
insert into T values ( S.CURRVAL, 5, 'host = ' || SYS_CONTEXT ('USERENV','HOST') ,sysdate);
insert into T values ( S.CURRVAL, 6, 'ip = ' || SYS_CONTEXT ('USERENV','IP_ADDRESS') ,sysdate);
insert into T values ( S.CURRVAL, 7, 'module = ' || SYS_CONTEXT ('USERENV','MODULE') ,sysdate);
insert into T values ( S.CURRVAL, 8, 'serverhost = ' || SYS_CONTEXT ('USERENV','SERVER_HOST') ,sysdate);


 l_n := ora_sql_txt( l_sql_text );
for i in 1 .. l_n
 LOOP
insert into t values ( s.CURRVAL,8+i, 'l_sql_text(' || i || ') = ' || l_sql_text(i),sysdate );
 end loop;

 end;

最佳答案

记录所有服务器错误是一个好主意,我已经看到它在生产环境中运行良好。

理论上,错误日志记录是由应用程序处理的。实际上,大多数应用程序并不能捕获所有数据库错误。拥有一个包含数据库生成的所有错误的表非常有用。

但是,这种触发器具有需要仔细考虑的特殊挑战:

  1. 敏感信息 - 如果查询有硬编码的社会安全号码,并且查询失败,则该号码将出现在错误日志中。确保您的组织能够承受该风险。不要授予每个人访问该表的权限。 (不要认为应用程序绑定(bind)变量可以避免这个问题。最有可能失败的查询是在应用程序外部运行的临时查询,它们将使用硬编码文字。)
  2. 不要责怪别人 - 您可能会对错误的数量感到惊讶。抵制责怪人们产生太多错误信息的冲动。如果你总是纠缠不相关的错误消息,你会激怒很多开发人员。 (这似乎是显而易见的,但我遇到过很多 DBA,他们只是喜欢提示任何人生成错误。这就是 Oracle 成为最令人讨厌的数据库的原因之一。)
  3. 格外仔细地测试 - 构建不当的系统事件触发器确实会毁坏数据库。最明显的问题是 LOGON 触发器,它可以有效地破坏整个数据库。在某些方面,AFTER SERVERERROR是一个更安全的事件,因为在调用之前某些东西已经损坏了。但奇怪的事情仍然可能发生。例如,您会注意到 exception when others then null;代码。该代码通常是反模式,但这是您真正想要抑制所有异常以避免无限循环的少数地方之一。
  4. 性能 - 错误带来的额外开销应该不重要。如果您的系统必须针对错误进行优化,那么您会遇到更严重的问题。但您可能需要担心 table 的大小。如果某个进程失控并在一天内发出一百万个无效查询,您不希望它消耗大量空间。 (这就是为什么默认情况下 Oracle 不会记录每个执行或每个错误。拒绝服务攻击的机会太多。)

示例架构

花一些时间设计更好的表格。忽略问题中使用的表的键值对类型。相反,创建一个表,每个错误存储一行,并使用有意义的名称。像这样的表将更容易进行有意义的查询:

--drop trigger audit_failed_trg;
--drop table server_errors;
--drop sequence server_error_seq;

create sequence server_error_seq;

create table server_errors
(
    id               number not null,
    error_date       date not null,
    ora_sysevent     varchar2(128),
    ora_login_user   varchar2(128),
    ora_server_error varchar2(4000),
    sid              number,
    host             varchar2(256),
    ip               varchar2(15),
    module           varchar2(4000),
    serverhost       varchar2(256),
    sql              clob,
    constraint server_errors_pk primary key(id)
);

触发器

create or replace trigger audit_failed_trg
after servererror on database
declare
    v_sql_text ora_name_list_t;
    v_sql      clob;
    v_n        number;
begin
    v_n := ora_sql_txt(v_sql_text);
    for i in 1 .. v_n loop
        v_sql := v_sql || v_sql_text(i);
    end loop;

    --If you find a huge number of irrelevant errors, you might want to filter them out here.

    insert into server_errors
    values
    (
        server_error_seq.nextval,
        sysdate,
        ora_sysevent,
        ora_login_user,
        ora_server_error(1),
        sys_context ('USERENV','SID'),
        sys_context ('USERENV','HOST'),
        sys_context ('USERENV','IP_ADDRESS'),
        sys_context ('USERENV','MODULE'),
        sys_context ('USERENV','SERVER_HOST'),
        v_sql
    );
    commit;

--Never raise an exception from this trigger.
--No matter what happens we don't want recursive errors.
exception when others then
    null;
end;
/

关于oracle - "after servererror on database trigger"是个好主意吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57026506/

相关文章:

sql - 优化一条Sql查询: filter an unindexed field

java - Java中的多线程导致表锁

SQL - 如果值存在于另一个表中,则插入到表中

java - 通过Web界面在Java中执行动态sql和pl/sql

asp.net-mvc - 在 asp.net-mvc 站点上优化 json 的最佳方法是什么

java - LibGDX:循环时使用 SpriteBatches

c# - 缓存已编译的 lambda 表达式

sql - Oracle 正则表达式忽略重复字符

使用 Maven 依赖项时出现 java.lang.ClassNotFoundException : oracle. jdbc.OracleDriver

oracle11g - Oracle 解释计划 :Cardinality returns a huge number but the query returns no records