这篇文章源于在PL/SQL领域非常有建树的专家:Steven Feuerstein 的推荐。
于是突发奇想,将该篇文章翻译成了中文。如果通过我的努力,确实让一些朋友有所收获,我将非常荣幸。
文章开始:
之前我的一些文章中,曾经建议通过使用11g中提供的FLASHBACK的数据归档功能而直接替换日志信息表(journalling tables)。即我们已经不再需要针对某些专门表中的记录,我们需要持续跟踪记录所有的改变和被改变之前的记录状态(可以参考如下链接 http://technology.amis.nl/blog/2453/oracle-11g-total-recall-flashback-in-the-hands-of-database-designers-and-application-developers-at-last-and-the-end-of-journalling-tables)。通过Flashbck的数据归档功能不但可以减少编程工作,而且通过该功能可以大幅提升DML操作的性能,同时又可获得许多的方便的功能。例如:Flashback Queries可以直接地象SQL查询一样查询历史数据,甚至通过使用dbms_flashback包,我们可以使用相同的应用程序和相同的查询操作去检索过去某个时间点的数据。
在之前的不太长的时间,Flashback有许多功能上的限制,而使得其基本上不能记录那些已经存有历史数据但结构又发生变化的表。随着11GR2在这些方面作的改进,绝大多数的限制都已经取消了,并且象增加、重命名以及删除字段和限制这样的对于表的DDL操作以已经可以实现。
另外一个导致日志表可以消失的原因,就是通过Flashback的Flashback Queries操作,可以得到一些重要的附加信息。何时(DML操作的时间戳)和操作信息(操作类型和原数据信息),这些信息可以很好的知道,操作者到底导致了怎样的变化。而这些在一个部门内的监管是非常重要的。过去,习惯使用数据库用户就是真实用户去提交交易。但是当今业界使用多层服务组件和WEB应用通过池化连接数据库,而造成数据库用户往往不是操作的真实用户。对于最终用户的操作通常(或者应该这样)通过sys_context(‘userenv’, ‘client_identifier’)可以获得,通过设置dbms_session.set_identifier() 或者直接使用连接(例如:JDBC)。对于我们的日志而言,我们需要记录或筛选真实的执行人和客户身份。
这篇文章描述了记录和后督交易时客户身份信息的方法。
标准的数据库审计工具(Standard Database Audit Facilities)
象其他一些人一样,我也不得不使用,我不甚熟悉的ORACLE数据库自带的审计功能。但让使用不够深入。但是,当试图检查过去某些数据库交易信息的时候,总是能够自然的想到使用使用审计功能。并且这种方法确实让我得到了我需要的信息:每个交易的操作记录,并且通过交易号(transactionid)可以关联到闪回的历史数据查询。这些数据会包含用户身份和执行的所有操作。Oralce数据库的审计可以进行许多种不同操作。从每种对象类型的建立、授权更新和执行操作,到每个数据库用户执行的操作甚至非法的对象。但是因为审计会产生大量的记录数据,而致使我们不得不定期清理这些历史数据。通常这些周期只能都是非常短暂的。为了记录交易历史,我尝试通过日志表转而使用审计功能。并且我需要确定审计的等级--那些操作和那些对象(是否还包含用户?)现在,我需要使用FLASHBAKC功能协助,就像我参考的那些文档,并且我需要确保每个针对EMP表的DML操作都必须被记录。这就意味着,下面的SQL开启审计功能:
- AUDIT INSERT, UPDATE, DELETE ON SCOTT.EMP
为了审计功能,我假设一个web应用,这个应用可以让真是的最终用户修改数据。在数据库中执行如下操作:设置用户身份,应用程序需要执行:
- begin
- dbms_session.set_identifier('TheRealUser');
- end;
进行下一个DML操作:
- update emp
- set sal = sal + 100
- where job ='CLERK'
当交易被提交后,EMP使用了新的值,而原有的值成为了历史数据。这些历史数据,可以通过Flashback Versions Query被我们查询。
- select ename
- , sal
- , versions_xid
- , VERSIONS_STARTTIME
- , VERSIONS_ENDTIME
- from emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE
- where job = 'CLERK'
结果集如下:
- ENAME SAL VERSIONS_XID VERSIONS_STARTTIME VERSIONS_ENDTIME
- ---------- ------ ---------------- ------------------------- -------------------------
- ADAMS 1405 07001E00A9040000 06-FEB-11 08.12.09.00 AM 06-FEB-11 08.42.13.00 AM
- ADAMS 1505 04000E0088040000 06-FEB-11 08.42.13.00 AM
- ADAMS 1400 06-FEB-11 08.12.09.00 AM
- JAMES 1255 07001E00A9040000 06-FEB-11 08.12.09.00 AM 06-FEB-11 08.42.13.00 AM
- JAMES 1355 04000E0088040000 06-FEB-11 08.42.13.00 AM
- JAMES 1250 06-FEB-11 08.12.09.00 AM
- MILLER 1605 07001E00A9040000 06-FEB-11 08.12.09.00 AM 06-FEB-11 08.42.13.00 AM
- MILLER 1705 04000E0088040000 06-FEB-11 08.42.13.00 AM
- MILLER 1600 06-FEB-11 08.12.09.00 AM
- SMITH 1105 07001E00A9040000 06-FEB-11 08.12.09.00 AM 06-FEB-11 08.42.13.00 AM
- SMITH 1205 04000E0088040000 06-FEB-11 08.42.13.00 AM
- SMITH 1100 06-FEB-11 08.12.09.00 AM
很显然,这些记录的数据中没有修改数据人的信息。但是,配合之前开启的审计追踪功能,我可以关联Flashback的Versions Query与 USER_OBJECT_AUDIT试图,找到更多的执行人(WHO)的数据信息:
- select ename
- , sal
- , uat.client_id
- , uat.os_username
- , uat.userhost
- , uat.username
- , uat.scn
- , versions_xid
- , VERSIONS_STARTTIME
- , VERSIONS_STARTSCN
- , VERSIONS_ENDTIME
- , VERSIONS_ENDSCN
- from emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE eh
- join
- user_audit_trail uat
- on ( eh.versions_xid = uat.transactionid)
- where job = 'CLERK'
- order
- by ename, versions_starttime
结果集显示客户身份(client identifier)信息与实际的操作用户不符。
- ENAME SAL CLIENT_ID OS_USERNAME USERHOST USERNAME SCN VERSIONS_XID VERSIONS_STARTTIME VERSIONS_STARTSCN
- ---------- ---------------------- ---------------------------------------------------------------- ------------------------
- ADAMS 1505 TheRealUser demo xp-vm SCOTT 2200769 04000E0088040000 06-FEB-11 08.42.13.00 AM 2200778
- JAMES 1355 TheRealUser demo xp-vm SCOTT 2200769 04000E0088040000 06-FEB-11 08.42.13.00 AM 2200778
- MILLER 1705 TheRealUser demo xp-vm SCOTT 2200769 04000E0088040000 06-FEB-11 08.42.13.00 AM 2200778
- SMITH 1205 TheRealUser demo xp-vm SCOTT 2200769 04000E0088040000 06-FEB-11 08.42.13.00 AM 2200778
注意:如果我想通过flash的versions query找到开启审计之前的历史数据,需要使用左外联接在我们的查询中。
- select ename
- , sal
- , uat.client_id
- , uat.os_username
- , uat.userhost
- , uat.username
- , uat.scn
- , versions_xid
- , VERSIONS_STARTTIME
- , VERSIONS_STARTSCN
- , VERSIONS_ENDTIME
- , VERSIONS_ENDSCN
- from emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE eh
- left outer join
- ( select uat.*
- , row_number() over (partition by transactionid order by extended_timestamp) rn
- from user_audit_trail uat
- ) uat
- on ( eh.versions_xid = uat.transactionid)
- where job = 'CLERK'
- and rn = 1
- order
- by ename, versions_starttime
现在,我已经介绍了一个嵌套SQL,如何通过一个查询将user_audit_trail所有交易都查询出来,而不是通过一个交易一个查询的方式进行检索。
这个方法确实非常简单。因为,我们可以象轻松开启审计信息那样开启每张表的Flashback功能。
但是这里却有一个审计开销缺点。首先是性能问题,因为对于每条被“触碰”到的记录,都会触发日志信息记录,同时存储在系统审计表SYS.AUD$中的数据量将是非常惊人的。因为审计将会收集所有用户和对象的操作。审计信息存储表需要周期性清除。注意,清理可以有选择行的指定某个对象或者操作。
为了简化标准的审计跟踪功能,我们可以构建一个JOB,周期性地从 sys.aud$表中抽取我们需要的记录信息,并构建自己需要的交易历史。因为审计机制会完整的记录每个交易的DML操作语句但我们只是需要每个交易的单个记录,我们可以通过整合针对每个交易记录的审计信息,达到大幅降低交易历史数据量的目的。交易历史表可以象如下:
- create table transaction_history
- ( transaction_id varchar2(100) not null
- , client_identifier varchar2(200)
- , os_user varchar2(200)
- , scn number
- , transaction_start_timestamp timestamp default systimestamp
- , db_user varchar2(100)
- , application_info varchar2(2000)
- )
从审计跟踪表中抽取交易历史数据操作:
- insert into transaction_history
- ( transaction_id
- , client_identifier
- , os_user
- , scn
- , transaction_start_timestamp
- , db_user
- )
- select uat.transactionid
- , uat.client_id
- , uat.os_username
- , uat.scn
- , uat.extended_timestamp
- , uat.username
- from ( select uat.*
- , row_number() over (partition by transactionid order by extended_timestamp) rn
- from user_audit_trail uat
- ) uat
- where rn = 1
- and transactionid is not null
对于审计跟踪(可能需要部分清理)和交易历史表(可能不能全部更新),我们可以将两者结合来构建基于EMP表的交易历史记录。
- select ename
- , sal
- , versions_xid
- , nvl(th.client_identifier, uat.client_id) client_id
- , nvl(th.os_user, uat.os_username) os_user
- , uat.userhost userhost
- , nvl(th.db_user, uat.username) db_user
- , nvl(th.scn,uat.scn) scn
- , VERSIONS_STARTTIME
- , VERSIONS_STARTSCN
- , VERSIONS_ENDTIME
- , VERSIONS_ENDSCN
- from emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE eh
- left outer join
- ( select uat.*
- , row_number() over (partition by transactionid order by extended_timestamp) rn
- from user_audit_trail uat
- ) uat
- on ( eh.versions_xid = uat.transactionid)
- left outer join
- transaction_history th
- on ( eh.versions_xid = th.transaction_id)
- where job = 'CLERK'
- and uat.rn = 1
- order
- by ename, versions_starttime
注意:查询可以部分的结合审计跟踪信息和我们自己的交易历史表在一个试图中。
上面提及的大概方法给我们提供一个针对标准应用构建日志表的思路。在这个举例中,我们并没有建立触发器。简单地开启针对某些表DML操作的审计跟踪,配以这些表的flashback归档数据,就足以构建我们的日志信息。在后续的文章中将会更为详细的介绍。
当然,如果我们要实际构建这样的日志信息也许还有一个难点需要克服,即关于DBA的审计,尤其是对于标准审计特性,对于DBA和对于应用开发人员的日志记录,我们的应用往往是没有考虑的(但是,我们需要他或她也去建立flashback的归档数据)。
在未来的文章中,我将会讨论看似相似的问题:跟踪交易历史的方法和通过flashback归档数据收集全面的日志信息。
----EOF 译文完。
原文链接:
http://technology.amis.nl/blog/10911/database-transaction-recorder-adding-who-to-when-and-what-to-make-flashback-take-over-from-journalling-tables
Thanks Lucas Jellema -:)