sql >> データベース >  >> RDS >> Oracle

Oracleでのテーブル変更の監視

    データベースのレプリケーションは、OracleからOracleへの構成に制限されなくなりました。 Oracle-to-cloudとOracle-to-BigQueryは、レプリケーション構成で選択できるさまざまなオプションの2つにすぎません。これらの構成の多くには、その汎用性と信頼性を考慮して、GoldenGateが最適なツールとして存在します。残念ながら、Oracleを別のプラットフォームに複製する場合、テーブルの変更などのアクションにより、モンキーレンチが動作する可能性があります。したがって、GoldenGate抽出物の異常終了を適切かつ迅速に処理することを見越して、このような変更を追跡することが望ましいでしょう。考えられるシナリオを見て、最善の行動方針を決定しましょう。

    DBAが最初に考えたのは、監査可能なアクションに関する豊富な情報を提供する統合監査です。残念ながら、「監査テーブル」は、監査に使用できる特権のリストには含まれていません。

    SCOTT @ orcl > create audit policy alter_tab_pol
      2  privileges alter table;
    privileges alter table
               *
    ERROR at line 2:
    ORA-46355: missing or invalid privilege audit option.
    
    
    SCOTT @ orcl >
    

    興味深いことに、「ALTERANYTABLE」特権は 監査可能ですが、監査されると思われるものは監査されません:

    SCOTT @ orcl > create audit policy table_pol
      2  privileges create any table, alter any table, drop any table;
    
    Audit policy created.
    
    SCOTT @ orcl > audit policy table_pol;
    
    Audit succeeded.
    
    SCOTT @ orcl > 
    

    そのようなポリシーは、他のユーザーへのそのような特権の付与を監査するだけであり、常に監査レコードを生成するとは限りません。要件は監査によってまだ満たされていないため、別のソリューションを作成する必要があります。幸い、Oracleは、そのようなアクションの監査レコードを生成できるシステムレベルのトリガーを提供しています。これがどのように行われるかの例を以下に示します。最初に、生成された監査レコードを含むテーブルが作成されます。

    create table ddl_log (
    operation   varchar2(30),
    obj_owner   varchar2(35),
    object_name varchar2(35),
    sql_text    varchar2(200),
    attempt_by  varchar2(35),
    attempt_dt  timestamp);
     
    create index ddl_log_idx 
    on ddl_log(obj_owner, operation);
    
    

    テーブルはobj_ownerと操作でインデックス付けされ、レポートの生成を高速化します。次に、実行されたすべてのCREATE、ALTER、およびDROPステートメントをログに記録するために、監視対象のテーブルを所有するユーザーとしてトリガーが作成されます。

    create or replace trigger ddl_trigger
    before create or alter or drop
    on schema
    
    declare
     oper ddl_log.operation%type;
     sql_text ora_name_list_t;
     i        pls_integer; 
    begin
      i := sql_txt(sql_text);
      if i = 1 then
            insert into ddl_log
            select ora_sysevent, ora_dict_obj_owner,
            ora_dict_obj_name, sql_text(1), user, v_systimestamp
            from dual;
      elsif i = 2 then
            insert into ddl_log
            select ora_sysevent, ora_dict_obj_owner,
            ora_dict_obj_name, sql_text(1)||sql_text(2), user, v_systimestamp
            from dual;
      elsif i >= 3 then
            insert into ddl_log
            select ora_sysevent, ora_dict_obj_owner,
            ora_dict_obj_name, sql_text(1)||sql_text(2)||sql_text(3), user, v_systimestamp
            from dual;
      end if;
    
    end ddl_trigger;
    /
    
    

    SQLテキストの64バイトの「ピース」の数は非常に多くなる可能性があるため、トリガーはSQL_TEXT列を最初の3つの「ピース」に制限し、文字列の最大長を192文字にします。より大きなステートメントで予想されるように、完全なテキストは提供されませんが、「altertable」ステートメント全体をキャプチャする必要があります。このトリガーは、ALTER TABLEステートメントだけでなく、データベースに送信されたCREATE / ALTER/DROPステートメントもキャプチャすることに注意してください。これは、ユーザーの変更、トリガーの変更、パッケージの変更、関数の変更、テーブルスペースの変更、システムの変更、…およびドロップ…ステートメントの作成もDDL_LOGテーブルに記録されることを意味します。このため、テーブルが急速に大きくなり、非常に大きくなる可能性があるため、有限の履歴を維持するための計画を作成する必要があります。ほとんどのシステムでは、データベース内のテーブルの変更を追跡するには90日で十分です。ログに記録されたデータから生成されたレポートは、削除される前に長期間(たとえば、12か月)保持できます。

    テーブルデータを管理するためのサンプルスクリプトを以下に示します。 90日間のデータウィンドウを適用します。ログディレクトリが作成されます:

    mkdir -p /u01/app/oracle/ddl_chg/purge_logs
    

    SQLスクリプトは、DDL_LOGから古いレコードを削除するように記述されています:

    column sys_date new_value dt noprint
    column name new_value db_nm noprint
    select to_char(sysdate,'RRRRMMDD') sys_date from dual;
    select name from v$database;
    
    spool /u01/app/oracle/ddl_chg/purge_logs/ddl_log_purge_$db_nm._&dt..log
    set echo on
    
    --
    -- Records slated for removal
    --
    select * From ddl_log where attempt_dt < sysdate - 90;
    
    --
    -- Delete selected records
    --
    delete from ddl_log where attempt_dt < sysdate - 90;
    
    commit;
    
    spool off
    set echo off
    

    これは明らかに、cron(または同様のスケジューラ)から直接実行できないため、ラッパースクリプトが必要です:

    #!/bin/ksh
    
    #
    # purge_ddl_log_90.sh
    #
    # Shell script to purge old audit records
    # from the DDL_LOG table
    #
    
    #
    # Find the selected database and set the environment
    #
    set -A database `ps -ef | grep [p]mon | grep '<name>' |  awk -F"_" '{print $3}'`
    
    for i in ${database[@]}
    
    #
    # Set the environment for the database
    #
    do
            ORACLE_SID=$i
            export ORACLE_SID
    
            ORAENV_ASK=NO
            export ORAENV_ASK
    
            unset ORACLE_BASE
            export ORACLE_BASE
    
            PATH=$PATH:<ORACLE_HOME/bin location>
    
            . <ORACLE_HOME/bin>/oraenv -s
    
            LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib:$ORACLE_HOME/precomp/public
            export LD_LIBRARY_PATH
    
            PATH=$ORACLE_HOME/bin:$PATH
            export PATH
    
    #
    # Start SQL*Plus and execute the script
    #
            sqlplus /nolog <<EOF
    connect / as sysdba
    @/u01/app/oracle/ddl_chg/purge_ddl_log_90.sql
    EOF
    
    done
    
    #
    # Make the output files readable for all
    *
    cd /u01/app/oracle/ddl_chg/purge_logs
    
    chmod 666 *.log
    
    #
    # Remove old purge logs
    #
    
    find . -name "purge*log" -mtime +365 -exec /bin/rm -rf {} ;
    
    

    シェルスクリプトは、psコマンドの出力に基づいて適切な環境とORACLE_SIDを設定します。スクリプトを編集して、検索するデータベース名とORACLE_HOMEの場所を指定する必要があります。 |を使用して複数のデータベース名を指定できます。セパレータとして:

    'abd|def|ghi|jkl'
    

    これにより、このテーブルとトリガーの組み合わせがインストールされているすべてのデータベースのDDL_LOGテーブルをパージする方法が提供されます。データベース名はログファイル名に含まれ、データベースごとにパージトレイルを個別に保持します。ログファイルを保持する時間の長さは、監視対象のシステムのストレージ制限を満たすように変更できます。

    変更レポートは、DDL_LOGテーブルにあるデータから生成できます:

    set linesize 140
    column sdate new_value sdt noprint
    select to_Char(sysdate, 'RRRRMMDDHH24')sdate from dual;
    
    column modlen new_value mlen noprint
    select 'a'||nvl(max(length(modification)),25) modlen From
    (select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
    from ddl_log
    where (instr(sql_text, 'alter table') > 0
    or instr(sql_text, 'ALTER TABLE') > 0));
    column objlen new_value olen noprint
    select 'a'||nvl(max(length(owner||'.'||tabname)),60) objlen From
    (select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
    from ddl_log
    where (instr(sql_text, 'alter table') > 0
    or instr(sql_text, 'ALTER TABLE') > 0));
    
    column modification format &mlen
    column mod_time format a29
    column tab_name format &olen
    
    select owner||'.'|| tabname tab_name, modification, mod_time
    from
    (select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'add ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0
    union
    select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'drop ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0
    union
    select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'modify ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0
    union
    select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'ADD ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0
    union
    select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'DROP ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0
    union
    select obj_owner owner, object_name tabname, substr(sql_text, instr(sql_text, 'MODIFY ')) modification, attempt_dt mod_time
    from ddl_log
    where instr(lower(sql_text), 'alter table') > 0) dl
    where lower(dl.modification) not like '%table%'
    and mod_time >= trunc(systimestamp)
    order by 1, 3
    
    spool /u01/app/oracle/ddl_chg/log/tab_chg_rpt_&sdt._&1..lst
    /
    spool off
    

    データベース名はスクリプトに渡されるため、レポートファイル名に含まれます。このコードは、テーブルの変更のみをレポートし(したがって、UNIONクエリの長​​い文字列)、以下に示すようなレポートを生成します。

    TAB_NAME         MODIFICATION                   MOD_TIME
    ---------------- ------------------------------ -----------------------------
    SCOTT.DDL_LOG    modify sql_text varchar2(200)  23-NOV-19 01.23.49.859971 PM
    

    また、スクリプトは、保存されたデータの最大長に基づいて列のフォーマットを設定し、行の長さを減らす可能性があります。タイムスタンプデータは、生成された変更レコードの日付と表示時間の両方の値を提供するために使用されました。これらのスクリプトはテスト済みですが、オペレーティングシステムベンダーによるLinux / Unixの実装に基づいて、いくつかの変更が必要になる場合があります。

    複製されたシステムを実行していないDBAの場合、これはあまり役に立たない可能性があります。ただし、Oracleから他のシステム(BigQuery、Snowflakeなど)にデータを複製する場合は、テーブルの変更がいつ発生したかを知ることで、それらの変更によって作成された複製の失敗に対処しやすくなります。レプリケーションプロセスがより速く軌道に戻ることができるほど、そのレプリケートされたデータに依存するシステムがより速く機能に戻ることができます。

    ###

    David Fitzjarrellの記事を見る


    1. PostgreSQLで中央値を計算する方法

    2. 行の最大値を見つける方法

    3. connection.select_valueは、pggemを使用したpostgresの文字列のみを返します

    4. Oracleで現在の日付を取得する方法