【IT168 技术文章】
简介
Oracle数据库10g提供了五个新的闪回功能:闪回版本查询,闪回事务查询,闪回删除,闪回表和闪回数据库。Oracle数据库11gR1提供了一个有趣的新的闪回功能:闪回数据存档,它允许一个Or acle数据库管理员维护一个记录,对指定时间范围内对所有表的的改变情况进行记录。
许多年前-事实上,在上个世纪-我是一名美国大西部饮料生产厂家人力资源系统的承包商。有一天,人力资源总监问我:“如何为我们的雇员数据配置一个delta表,使我们能够跟踪所有对雇员数据的更改情况?”出现在我脑袋中的答案就像最近的美国总统大选期间CNN用来显示和分析投票结果的“神奇地图”一样:
我想创建一个名为EMPLOYEES_HISTORY的表,除了增加了一个相应的时间戳记的日期和时间的变化外,它与Employees表的列完全一样。
我希望把这个新表放在一个单独的表空间。
我想套用外键参照完整性约束EMPLOYEES_HISTORY表,以确保两个表之间的数据一致。
最后,我想创建一个AFTER ... FOR EACH ROW触发器,无论何时在Employees表上发生INSERT,UPDATE或DELETE事务时,都将在EMPLOYEES_HISTORY表中插入delta记录。
然后一些潜在的问题开始萦绕着我,这样做了会发生什么,我不停地问我自己,如果某人:
禁用了EMPLOYEES_HISTORY表上的触发器,并且忘记重新开启了,会发生什么事情?
需要向EMPLOYEES表中增加新列,或修改数据类型,但忘记同步修改EMPLOYEES_HISTORY表上对应的列了,会发生什么?
虽然同时对EMPLOYEES和EMPLOYEES_HISTORY表做了一致的修改,但忘记修改触发器了,会发生什么?
如果需要删除EMPLOYEES表的列或将其标记为UNUSED时,会发生什么?
意外TRUNCATE掉EMPLOYEES_HISTORY表时会发生什么?
意外删除了EMPLOYEES_HISTORY表时会发生什么?甚至更糟糕的是删除了EMPLOYEES表时会发生什么?
这些问题的简单答案是,如果真发生了这些事情,我不得不向客户解释为什么弄丢了EMPLOYEES_HISTORY表中的delta数据。
用闪回数据归档实现Oracle“完全撤销”
如果我拥有Oracle 11g,我会直接告诉客户我花几分钟就能恢复数据,Oracle 11gR1新的闪回数据归档(FBDA)功能创建一个独立的仓库保留表中数据改变的历史记录。
FBDA信息存储在一套独立的对象中,跟踪目标表的事务历史,这些对象存储在一个或多个表空间中,FBDA表的名称是由系统自动产生的,可以通过视图DBA_FLASHBACK_ARCHIVE_TABLES查看,一旦在表上开启了FBDA,所有保留的事务历史都可以查看,这样就不用再为关键事务表创建对应的历史跟踪表了,那样做很麻烦,因为创建历史跟踪表后,还有编写复杂的触发器,确保历史数据被精确跟踪。
历史信息的捕捉也是非常高效的,因为Oracle 11g专门修改了内核,使保留历史数据的性能开销减到最小,此外,这些历史数据以压缩格式存储,减少了存储的需要,另外,只要在表上开启了FBDA,Oracle 11g就不会再对历史数据做使其无效的操作(如删除或清空目标表)或阻止捕捉(如删除目标表的列)。
下面解释一下FBDA是如何工作的:
闪回数据归档(FBDA)是一个新的后台进程,负责跟踪和归档开启了FBDA的表的历史数据,它通过异步处理自动收集和向指定的闪回数据归档写入原始数据。
当开启FBDA的表中任何数据发生变化时,FBDA首先询问存储在数据库缓冲区中的UNDO数据,如果数据仍然在那里,FBDA就会使用它,如果UNDO数据已经从数据库缓冲区中移除,FBDA就会尝试从UNDO表空间中的UNDO段中获取数据发生的变化。
当FBDA捕捉到变化数据时,它会整理开启FBDA的表中的行,然后将这些行写入FBDA中的历史表,这些表数据经过压缩,就和内部分区一样。
有趣的是,FBDA不包括捕获数据的原始索引,当在FBDA中可以对历史数据创建另一个索引。
自动保留策略:按照类似的保留需求,可以将FBDA对象中的历史表数据聚集在一起,Oracle 11g也提供了自动清理FBDA中数据的方法,一旦超出了指定的保留期限就会自动执行清理工作,多个表可以共享同一个数据保留和清理策略,因为FBDA是由一个或多个表空间构成的,所以可以创建多个FBDA,每个FBDA指定不同的保留期限,这样就可以创建多个FBDA满足不同需求的保留策略,下面是一些常见的示例:
为普通的短期历史查询保留90天
为普通的长期历史查询保留1整年
为国家法律需要保留20年
配置闪回数据归档
准备一个Oracle 11g数据库使用FBDA功能是相当简单的,只需要经过几个简单的步骤即可:
(1)创建或指定一个或多个表空间用于FBDA保留历史数据
(2)随意指派一个FBDA作为数据库的默认FBDA
(3)指派一个用户账户作为FBDA管理员,授予它FLASHBACK ARCHIVE ADMINISTER系统权限
(4)授予FBDA权限给适合的用户账号
(5)授予FLASHBACK和SELECT权限给合适的FBDA表用户
(6)为FBDA用户授予DBMS_FLASHBACK存储过程EXECUTE权限
下面是具体的实现命令:
DROP USER fbda_admin CASCADE;
CREATE USER fbda_admin IDENTIFIED BY fbda_admin;
GRANT FLASHBACK ARCHIVE ADMINISTER TO fbda_admin;
-- 授予其它用户合适的闪回权限
GRANT FLASHBACK ANY TABLE TO hr;
GRANT EXECUTE ON DBMS_FLASHBACK TO hr;
GRANT FLASHBACK ANY TABLE TO oe;
GRANT EXECUTE ON DBMS_FLASHBACK TO oe;
GRANT FLASHBACK ANY TABLE TO sh;
GRANT EXECUTE ON DBMS_FLASHBACK TO sh;
-- 为闪回数据归档创建表空间
DROP TABLESPACE fbda INCLUDING CONTENTS AND DATAFILES;
CREATE TABLESPACE fbda
DATAFILE '/u01/app/oracle/oradata/ORCL/tsp_fdba01.dbf'
SIZE 24M;
-- 创建一个闪回数据归档保留5天有价值的历史
DROP FLASHBACK ARCHIVE fbda_1;
CREATE FLASHBACK ARCHIVE fbda_1
TABLESPACE fbda
QUOTA 1M
RETENTION 5 DAY;
GRANT FLASHBACK ARCHIVE ON fbda_1 TO hr;
GRANT FLASHBACK ARCHIVE ON fbda_1 TO oe;
GRANT FLASHBACK ARCHIVE ON fbda_1 TO sh;
-- 创建一个闪回数据归档保留1整年有价值的历史
DROP FLASHBACK ARCHIVE fbda_2;
CREATE FLASHBACK ARCHIVE fbda_2
TABLESPACE fbda
QUOTA 4M
RETENTION 1 YEAR;
GRANT FLASHBACK ARCHIVE ON fbda_2 TO hr;
GRANT FLASHBACK ARCHIVE ON fbda_2 TO oe;
GRANT FLASHBACK ARCHIVE ON fbda_2 TO sh;
-- 创建一个闪回数据归档保留7年有价值的历史
DROP FLASHBACK ARCHIVE fbda_3;
CREATE FLASHBACK ARCHIVE fbda_3
TABLESPACE fbda
QUOTA 20M
RETENTION 7 YEAR;
GRANT FLASHBACK ARCHIVE ON fbda_3 TO hr;
GRANT FLASHBACK ARCHIVE ON fbda_3 TO oe;
GRANT FLASHBACK ARCHIVE ON fbda_3 TO sh;
从上面的代码可以看出,其实使用闪回数据归档真的很简单,按照上面的代码,我创建一个表空间FBDA,并在它里面创建了三个闪回数据归档:FBDA_A,FBDA_2和FBDA_3,分别保留5天,1年,7年,我还创建了一个新用户账号FBDA_ADMIN,并授予它FLASHBACK ARCHIVE ADMINISTER权限,最后,我们给系统中“sample”方案中的HR,OE和SH用户账号授予了合适的系统权限,以便它们也可以参与FBDA操作。
启用和禁用一个表的历史保留能力:使用ALTER TABLE <表名> FLASHBACK ARCHIVE [FBDA名]; 命令在现有表上启用FBDA。
如果没有指定FBDA名,将会使用默认的FBDA
DBA也可以给表指定想要的FBDA
如果不存在默认的FBDA就必须要明确指定FBDA
同样,使用ALTER TABLE
ALTER FLASHBACK ARCHIVE fbda_2 SET DEFAULT;
-- 将FBDA_1改为默认的FBDA,注意FBDA_1和FBDA_2只会简单地交换名称
ALTER FLASHBACK ARCHIVE fbda_1 SET DEFAULT;
-- 启用一个现有表使用默认的FBDA(fbda_1)
ALTER TABLE hr.applicants FLASHBACK ARCHIVE;
-- 启用表使用指定的FBDA
ALTER TABLE hr.departments FLASHBACK ARCHIVE fbda_1;
ALTER TABLE hr.job_history FLASHBACK ARCHIVE fbda_2;
ALTER TABLE oe.customers FLASHBACK ARCHIVE fbda_3;
-- 在指定表上禁用闪回数据归档
ALTER TABLE hr.departments NO FLASHBACK ARCHIVE;
上面的代码说明了以下基本原理:
可以事后修改默认的FBDA。最初我使用FBDA_2作为默认FBDA,后来我使用ALTER FLASHBACK ARCHIVE
接下来,我在表HR.APPLICANTS上使用默认FBDA开启了闪回数据归档跟踪
然后,我对HR.DEPARTMENTS,HR.JOB_HISTORY和OE.CUSTOMERS表开启了闪回数据归档跟踪,为它们的历史数据选择了不同的FBDA。
最后,为了显示如何在表上中断FBDA跟踪,我再次在OE.CUSTOMERS 表上使用了ALTER TABLE
闪回数据归档:历史数据和元数据
Oracle 11g在FBDA中存储数据时没有“重新设计车轮”,每个启用FBDA的表使用三个简单的表结构,每个都以<源表所有者>.SYS_FBA_<目的>_格式命名,如表1所示。这些表中的数据可以直接查询,对于那些想一探Oracle 11g是如何管理FBDA基础结构的人来说非使用这些表不可。
表1.闪回数据归档:历史数据存储
为了便于解说,我首先在HR.APPLICANTS表上使用DML语句对其做了一个改变,然后我清洗了数据库缓冲区,并使用ALTER SYSTEM SET UNDO_TABLESPACE=... SCOPE=BOTH; 命令将UNDO过程切换到一个不同的表空间上,请看下面的代码:
UPDATE hr.applicants
SET salary_desired = ROUND(salary_desired, -3)
WHERE MOD(applicant_id,5) = 0;
COMMIT;
-- 对最接近100美元的申请者申请的薪水进行四舍五入
UPDATE hr.applicants
SET salary_desired = ROUND(salary_desired, -2)
WHERE MOD(applicant_id,5) <> 0
AND MOD(applicant_id,3) = 0;
COMMIT;
-- 现在清空数据库缓冲区和当前UNDO表空间的内容,确保闪回版本查询从闪回数据归档中返回HR.APPLICANTS表的内容
DROP TABLESPACE smallundo INCLUDING CONTENTS AND DATAFILES;
CREATE UNDO TABLESPACE smallundo
DATAFILE '/u01/app/oracle/oradata/ORCL/tsp_smallundo01.dbf'
SIZE 2M;
ALTER SYSTEM CHECKPOINT;
ALTER SYSTEM FLUSH BUFFER_CACHE;
ALTER SYSTEM SET UNDO_TABLESPACE = 'SMALLUNDO' SCOPE=BOTH;
TTITLE 'Sample Data from Flashback Data Archives|(From SYS_FBA_HIST_73218)'
COL applicant_id FORMAT 99999 HEADING 'Appl|ID'
COL endscn FORMAT 9999999 HEADING 'Ending|SCN'
COL last_name FORMAT A20 HEADING 'Last Name'
COL gender FORMAT A2 HEADING 'G|N|D|R'
COL salary_desired FORMAT 999999.99 HEADING 'Salary|Desired'
COL job_desired FORMAT A10 HEADING 'Job|Desired'
SELECT
applicant_id
,endscn
,last_name
,gender
,salary_desired
,job_desired
FROM HR.SYS_FBA_HIST_73218
WHERE applicant_id < 10
ORDER BY applicant_id, endscn;
TTITLE OFF
TTITLE 'Sample Data from HR.APPLICANTS|(Between Time Periods)'
COL applicant_id FORMAT 99999 HEADING 'Appl|ID'
COL versions_endscn FORMAT 9999999 HEADING 'Ending|SCN'
COL last_name FORMAT A20 HEADING 'Last Name'
COL gender FORMAT A2 HEADING 'G|N|D|R'
COL salary_desired FORMAT 999999.99 HEADING 'Salary|Desired'
COL job_desired FORMAT A10 HEADING 'Job|Desired'
SELECT
applicant_id
,VERSIONS_ENDSCN
,last_name
,gender
,salary_desired
,job_desired
FROM hr.applicants
VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('2008-12-04 04:28','yyyy-mm-dd hh24:mi')
AND TO_TIMESTAMP('2008-12-04 04:55','yyyy-mm-dd hh24:mi')
WHERE APPLICANT_ID < 10
ORDER BY applicant_id, versions_endscn;
TTITLE OFF
当UNDO表空间成功切换后,我查询SYS_FBA_HIST_73218表获取最近的UNDO事务,结果如下:
(From SYS_FBA_HIST_73218)
G
N
Appl Ending D Salary Job
ID SCN Last Name R Desired Desired
------ -------- -------------------- -- ---------- ----------
3 1203058 Brown M 70113.04 IT_CNTR2
6 1203058 Chandler M 55511.77 IT_CNTR1
9 1203058 Chestnut M 73042.53 IT_CNTR3
直接从表本身查询,对比如下:
(Between Time Periods)
G
N
Appl Ending D Salary Job
ID SCN Last Name R Desired Desired
------ -------- -------------------- -- ---------- ----------
1 Aniston M 88017.94 IT_CNTR2
2 Niven M 82553.39 IT_CNTR1
3 1203058 Brown M 70113.04 IT_CNTR2
3 Brown M 70100.00 IT_CNTR2
4 Murdock M 70389.16 IT_CNTR2
5 1202273 Bedelia M 38720.86 IT_CNTR3
5 Bedelia M 39000.00 IT_CNTR3
6 1203058 Chandler M 55511.77 IT_CNTR1
6 Chandler M 55500.00 IT_CNTR1
7 Lerner M 80587.46 IT_CNTR2
8 Robinson M 49516.37 IT_CNTR3
9 1203058 Chestnut M 73042.53 IT_CNTR3
9 Chestnut M 73000.00 IT_CNTR3
FBDA元数据:Oracle 11gR1提供了几个关于FBDA元数据的数据字典视图,包括哪个表空间支持可扩展的历史数据存储,以及FBDA中保留了哪个表:
表2.闪回数据归档:元数据
使用闪回数据归档:审计和数据修复
至此,闪回数据归档已经建立起来,但有什么用途呢?下面就列举几个情景来说明它的用途:
审计历史事务
现在我们已经在HR.APPLICANTS表上建立起FBDA跟踪了,所有发生变化的数据将会自动保留下来,这样我们就可以向同等雇佣机会委员会(EEOC)证明我们在招聘人员时没有歧视,因为我们最近和美国联邦政府签订了这样一份协议,以后我就可以拿事实数据进行说明了。
数据粉碎
相信大部分对于保留历史数据在法律上的重要性都有深刻的理解,好的记账原则要求至少保留关键财务数据达7年之久,方便国家税务机关审计。Oracle 11g将会自动删除超出保留期限的数据,在数据粉碎期间,只针对历史数据,而不是FBDA自身。
修复丢失的或错误修改的数据
以我过去30年的IT经历来看,很多时候用户、程序开发人员甚至DBA可能不经意错误地修改了关键数据,甚至物理地删除了关键表中的行,更可怕的是,这些错误可能过了很久才被发现,那个时候可能最希望能够如魔法般地重建数据,这放在过去,只能不完全恢复数据,闪回数据当然也支持不完全恢复,但它的粒度是数据库和指定的SCN(但前提是在犯错前已经开启了闪回日志功能),闪回表仍然受限于当前UNDO表空间UNDO保留的数量。下面给出一段代码显示如何使用闪回数据归档数据和闪回查询来找回丢失的数据的:
WHERE application_date <= TO_DATE('11-10-2008','dd-mm-yyyy');
COMMIT;
INSERT INTO hr.applicants
SELECT *
FROM hr.applicants
VERSIONS BETWEEN TIMESTAMP TO_TIMESTAMP('2008-12-04 10:00','yyyy-mm-dd hh24:mi')
AND MAXVALUE
WHERE VERSIONS_OPERATION = 'D';
COMMIT;
维护闪回数据归档
虽然闪回数据归档自身可以完成维护,但有时还是需要手动维护,如:
扩大现有FBDA的大小(新的大小不能超过授予FBDA管理用户限额的最大表空间尺寸)。
改变FBDA上历史数据保留策略。
手动清洗FBDA内比指定日期时间还旧的数据。
删除现有FBDA(注意删除FBDA时并没有删除对应的表空间)。
请看下面的代码:
ALTER FLASHBACK ARCHIVE fbda_1
PURGE BEFORE TIMESTAMP(SYSTIMESTAMP - INTERVAL '1' DAY);
-- 修改现有FBDA的空间限额,减小到2M
ALTER FLASHBACK ARCHIVE fbda_1
MODIFY TABLESPACE fbda QUOTA 2M;
-- 减少现有FBDA的保留期限为90天
ALTER FLASHBACK ARCHIVE fbda_3
MODIFY RETENTION 90 DAY;
-- 给现有FBDA增加一个无空间限额的新表空间,这样就允许FBDA使用新增加表空间的所有可用空间
DROP TABLESPACE fbda_extd INCLUDING CONTENTS AND DATAFILES;
CREATE TABLESPACE fbda_extd
DATAFILE '/u01/app/oracle/oradata/ORCL/tsp_fdba_extd01.dbf'
SIZE 16M;
ALTER FLASHBACK ARCHIVE fbda_1
ADD TABLESPACE fbda_extd;
-- 删除一个现有FBDA,注意对应的表空间仍然存在
DROP FLASHBACK ARCHIVE fbda_1;
FBDA空间管理:当一个FBDA用尽了所有可用的空间时,由这个FBDA支持的表如果发生修改操作时,其会话会接收到一个或两个错误消息(下面用fbda_1来解释这两个错误):
ORA-55617: Flashback Archive fbda_1 runs out of space and tracking on fda1 is suspended
这个错误消息指出了哪个FBDA空间几乎用完了,当FBDA达到90%或更高时就会报这个错误。
ORA-55623: Flashback Archive fbda_1 is blocking and tracking on all tables is suspended
这种情况下,FBDA已经完全用完了可用空间。
不管出现哪个错误,DBA都可以手动增加FBDA的限额,或直接增加FBDA所在表空间的大小,注意这些错误也会记录到Alert.log文件中。
闪回数据归档:限制和建议
你可能已经猜到这么强大的功能肯定会有限制条件的,不错,下面就列举出这些限制条件:
DDL限制
如果在开启FBDA的表上应用下面这些DDL命令,Oracle 11g将会产生一个异常:
(1)ALTER TABLE <表名>命令,如
删除一列
重命名列
修改列
执行PARTITION 或SUBPARTITION 操作
将列的数据类型从LONG转换为LOB
调用UPGRADE TABLE操作,不管是否指定了INCLUDING DATA选项
(2)DROP TABLE <表名>;
(3)RENAME TABLE <表明>;
(4)TRUNCATE TABLE <表名>;
非常好的实践
最后,我还是列出在Oracle 11g中使用闪回数据归档特性时推荐的非常好的实践:
(1)在查询以往数据之前执行一下COMMIT 或ROLLBACK 操作,这样可以确保数据库的一致性。
(2)闪回数据归档进程总是使用当前会话设置,包括NLS设置如NLS_LANGUAGE和NLS_CHARACTERSET,但实际中当历史数据被保留时,这些变量的设置可能并不匹配。
(3)Oracle推荐使用INTERVAL和TIMESTAMP变量转换函数来估算过去的时间,如指定SYSTIMESTAMP – INTERVAL ‘20’ DAYS 来获得启用FBDA特性的表过去的数据。
(4)为了更精确地查询FBDA中的数据,Oracle推荐使用SCN,记住TIMESTAMP_TO_SCN函数可以用来直接从TIMESTAMP值中获得一个相对准确的SCN值,但它的精确度也只能达到3秒左右。
小结
Oracle 11g新的闪回数据归档特性让DBA有能力将历史数据保留非常长的时间,只要保存历史数据的表空间的容量足够大,因为闪回查询、闪回版本查询和闪回事务查询也受到支持,因此Oracle DBA也可以利用FBDA特性来纠正对数据的错误修改。FBDA安装容易,监控简单,自我维护,相信它会成为Oracle DBA受欢迎的工具。