zoukankan      html  css  js  c++  java
  • 实现trigger集中记录所有库ddl操作

    转载自:http://www.xifenfei.com/3380.html

    今天客户说了一个我感觉有意思的需求:在一个库上的一张表记录所有库的ddl操作,实现方式:在一个库上建立表和触发器,其他库上通过dblink+同义词+触发器实现ddl操作记录到远程的表中.他当时写了一个触发器,但是有错误,想让我协助解决.在我们的一起努力下,解决了该触发器在dblink同义词的库上出错的问题.我这里测试使用的是10g的库做为存储所有库的ddl记录的库,11g库做为一个通过dblink插入ddl操作记录的库.
    在10g数据库库中操作
    1.创建记录ddl操作表

    SQL> conn chf/xifenfei
    Connected.
    SQL> create table t_ddl_audit(
      2  db_name varchar2(30),
      3  login_user varchar2(30),
      4  ddl_time date,
      5  ip_address varchar2(20),
      6  audsid varchar2(20),
      7  schema_user varchar2(30),
      8  schema_object varchar2(40),
      9  login_tool varchar2(40),
     10  os_user varchar2(40),
     11  ddl_sql varchar2(4000));
    
    Table created.
    

    2.创建触发器

    SQL> create or replace trigger tri_ddl_audit
      2    before ddl on database
      3  declare
      4    n           number;
      5    str_stmt    varchar2(4000);
      6    sql_text    ora_name_list_t;
      7    l_trace     number;
      8    v_module    varchar2(50);
      9    v_action    varchar2(50);
     10    str_session v$session%rowtype;
     11  begin
     12    n := ora_sql_txt(sql_text);
     13    for i in 1 .. n loop
     14      str_stmt := substr(str_stmt || sql_text(i), 1, 3000);
     15    end loop;
     16    dbms_application_info.READ_MODULE(v_module, v_action);
     17    INSERT INTO chf.t_ddl_audit
     18      (db_name,
     19       login_user,
     20       ddl_time,
     21       ip_address,
     22       audsid,
     23       schema_user,
     24       schema_object,
     25       login_tool,
     26       os_user,
     27       ddl_sql)
     28    VALUES
     29      (sys_context('USERENV', 'db_name'),
     30       ora_login_user,
     31       SYSDATE,
     32       sys_context('USERENV', 'IP_ADDRESS'),
     33       userenv('SESSIONID'),
     34       ora_dict_obj_owner,
     35       ora_dict_obj_name,
     36       v_module,
     37       sys_context('userenv', 'os_user'),
     38       str_stmt);
     39  exception
     40    when no_data_found then
     41      null;
     42  end;
     43  /
    
    Trigger created.
    

    3.测试触发器

    SQL> conn chf/xifenfei
    Connected.
    SQL> create table t_xff as select * from dba_tables where rownum=1;
    
    Table created.
    
    SQL> select db_name,login_user,ddl_sql from t_ddl_audit;
    
    DB_NAME                        LOGIN_USER
    ------------------------------ ------------------------------
    DDL_SQL
    -----------------------------------------------------------------
    XFF                            CHF
    create table t_xff as select * from dba_tables where rownum=1
    

    在11g数据库中操作
    1.创建dblink和同义词

    SQL> create database link "ora10g_dblink"
      2   connect to chf
      3    identified by "xifenfei"
      4     using 'ora10g';
    
    Database link created.
    
    SQL> create  synonym t_ddl_audit for t_ddl_audit@ora10g_dblink;
    
    Synonym created.
    

    2.第一次创建触发器

    SQL> create or replace trigger tri_ddl_audit
      2    before ddl on database
      3  declare
      4    n           number;
      5    str_stmt    varchar2(4000);
      6    sql_text    ora_name_list_t;
      7    l_trace     number;
      8    v_module    varchar2(50);
      9    v_action    varchar2(50);
     10    str_session v$session%rowtype;
     11  begin
     12    n := ora_sql_txt(sql_text);
     13    for i in 1 .. n loop
     14      str_stmt := substr(str_stmt || sql_text(i), 1, 3000);
     15    end loop;
     16    dbms_application_info.READ_MODULE(v_module, v_action);
     17    INSERT INTO t_ddl_audit
     18      (db_name,
     19       login_user,
     20       ddl_time,
     21       ip_address,
     22       audsid,
     23       schema_user,
     24       schema_object,
     25       login_tool,
     26       os_user,
     27       ddl_sql)
     28    VALUES
     29      (sys_context('USERENV', 'db_name'),
     30       ora_login_user,
     31       SYSDATE,
     32       sys_context('USERENV', 'IP_ADDRESS'),
     33       userenv('SESSIONID'),
     34       ora_dict_obj_owner,
     35       ora_dict_obj_name,
     36       v_module,
     37       sys_context('userenv', 'os_user'),
     38       str_stmt);
     39  exception
     40    when no_data_found then
     41      null;
     42  end;
     43  /
    
    Trigger created.
    

    3.测试触发器

    SQL> create table t_xff as select * from dba_objects where rownum<10;
    create table t_xff as select * from dba_objects where rownum<10
                                        *
    ERROR at line 1:
    ORA-00604: error occurred at recursive SQL level 1
    ORA-02070: database  does not support  in this context
    ORA-06512: at line 15
    

    出现ORA-02070错误,估计是类此sys_context(‘userenv’, ‘os_user’)导致。
    4.第二次创建触发器

    SQL> create or replace trigger tri_ddl_audit
      2    before ddl on database
      3  declare
      4    n           number;
      5    str_stmt    varchar2(4000);
      6    sql_text    ora_name_list_t;
      7    l_trace     number;
      8    v_module    varchar2(50);
      9    v_action    varchar2(50);
     10    v_db_name   varchar2(50);
     11    v_ip_addr   varchar2(50);
     12    v_os        varchar2(50);
     13    v_session_id varchar2(50);
     14    str_session v$session%rowtype;
     15  begin
     16    n := ora_sql_txt(sql_text);
     17    for i in 1 .. n loop
     18      str_stmt := substr(str_stmt || sql_text(i), 1, 3000);
     19    end loop;
     20    dbms_application_info.READ_MODULE(v_module, v_action);
     21    v_db_name :=sys_context('USERENV', 'db_name');
     22    v_ip_addr :=sys_context('USERENV', 'IP_ADDRESS');
     23    v_os:=sys_context('userenv', 'os_user');
     24    v_session_id:=userenv('SESSIONID');
     25    INSERT INTO t_ddl_audit
     26      (db_name,
     27       login_user,
     28       ddl_time,
     29       ip_address,
     30       audsid,
     31       schema_user,
     32       schema_object,
     33       login_tool,
     34       os_user,
     35       ddl_sql)
     36    VALUES
     37      (v_db_name,
     38       ora_login_user,
     39       SYSDATE,
     40       v_ip_addr,
     41      v_session_id,
     42       ora_dict_obj_owner,
     43       ora_dict_obj_name,
     44       v_module,
     45       v_os,
     46       str_stmt);
     47  exception
     48    when no_data_found then
     49      null;
     50  end;
     51  /
    
    Trigger created.
    

    5.继续测试触发器

    SQL> drop table t3;
    drop table t3
    *
    ERROR at line 1:
    ORA-00604: error occurred at recursive SQL level 1
    ORA-02069: global_names parameter must be set to TRUE for this operation
    ORA-06512: at line 23
    

    根据ORA-02069,查询资料发现是通过dblink插入数据使用了变量和常量的方式混合使用导致该错误,修改触发器全部使用变量方式
    6.第三次创建触发器

    SQL> create or replace trigger tri_ddl_audit
      2    before ddl on database
      3  declare
      4    n           number;
      5    str_stmt    varchar2(4000);
      6    sql_text    ora_name_list_t;
      7    l_trace     number;
      8    v_module    varchar2(50);
      9    v_action    varchar2(50);
     10    v_db_name   varchar2(50);
     11    v_ip_addr   varchar2(50);
     12    v_os        varchar2(50);
     13    v_session_id varchar2(50);
     14    v_loginuser    varchar2(50);
     15     v_obj_name varchar2(50);
     16    v_owner    varchar2(50);
     17    str_session v$session%rowtype;
     18  begin
     19    n := ora_sql_txt(sql_text);
     20    for i in 1 .. n loop
     21      str_stmt := substr(str_stmt || sql_text(i), 1, 3000);
     22    end loop;
     23    dbms_application_info.READ_MODULE(v_module, v_action);
     24    v_db_name :=sys_context('USERENV', 'db_name');
     25    v_ip_addr :=sys_context('USERENV', 'IP_ADDRESS');
     26    v_os:=sys_context('userenv', 'os_user');
     27    v_session_id:=userenv('SESSIONID');
     28    v_loginuser:= ora_login_user;
     29    v_owner:=ora_dict_obj_owner;
     30    v_obj_name:=ora_dict_obj_name;
     31    INSERT INTO t_ddl_audit
     32      (db_name,
     33       login_user,
     34       ddl_time,
     35       ip_address,
     36       audsid,
     37       schema_user,
     38       schema_object,
     39       login_tool,
     40       os_user,
     41       ddl_sql)
     42    VALUES
     43      (v_db_name,
     44       v_loginuser,
     45       SYSDATE,
     46       v_ip_addr,
     47      v_session_id,
     48       v_owner,
     49       v_obj_name,
     50       v_module,
     51       v_os,
     52       str_stmt);
     53  exception
     54    when no_data_found then
     55      null;
     56  end;
     57  /
    
    Trigger created.
    

    7.测试触发器

    SQL> create table t_xff11 as select * from dba_tables where rownum<10;
    
    Table created.
    
    SQL> select db_name,login_user,ddl_sql from t_ddl_audit;
    
    DB_NAME                        LOGIN_USER
    ------------------------------ ------------------------------
    DDL_SQL
    -----------------------------------------------------------------
    ora11g                         CHF
    create table t_xff11 as select * from dba_tables where rownum<10
    
    XFF                            CHF
    create table t_xff as select * from dba_tables where rownum=1
    

    补充说明
    这个方案个人感觉是一个实验室中的方案,在实际的生成环境中很难应用上
    1.trigger记录ddl操作本身效率不高
    2.如果某个库不能访问存储ddl操作的表的数据库,将导致该数据库所有ddl操作hang住,从而可能使得该数据库hang住的风险

  • 相关阅读:
    初识多线程
    java开发中我们经常用到的一些名词
    gitHub提交代码
    Java-基础-HashMap
    Java-基础-LinkedList
    Java-基础-ArrayList
    Java-基础-JDK动态代理
    Java-基础-反射
    RabbitMQ-延迟队列
    RabbitMQ-TTL-死信队列_DLX
  • 原文地址:https://www.cnblogs.com/wangn/p/3041795.html
Copyright © 2011-2022 走看看