在闪回恢复操作中,有一个重要的数据结构起着关键性的作用,这个对象就是SMON_SCN_TIME。
SMON_SCN_TIME是Oracle数据库的系统表,SMON每5分钟更新一次这张表,记录一个时间戳(Timestamp)和当前的SCN,这个表共保存1440条记录,也就是5天的信息,通过这个表,数据库可以对5天之内的SCN和时间进行粗略的对应和转换。
这个表被用于进行辅助恢复等功能,闪回查询就依赖于这个系统表,SMON_SCN_TIME是数据库的一个内部表,Oracle在不同版本中都可能变更这个表的内容和作用。
一、Oracle 9i中的SMON_SCN_TIME
看一下Oracle9iR2中这个表的结构,从数据库创建脚本sql.bsq文件中可以找到这个对象的创建过程:
rem create the scn-->time tracking table that smon will maintain rem as a circular queue - notice that we populate the entire rem table with the 1440 entries required (5* 60 sec * 60 min * 24 hr / 5 per min rem in this way, smon can do a single update statement every 5 minutes rem to update a single row and the table will have mapping info for 5 days. rem the table is also partitioned via thread number rem so that each instance maintains a local (or recent) scn to time mapping
复制
以上的注释部分说明了这个表的用途,也就是通过这个表构建了scn->time的跟踪关系,而且对于不同的实例,根据thread号进行区分以便每个实例都能维护一个scn到时间的映射关系。
以下创建过程首先创建了一个cluster,然后再创建了smon_scn_time表:
create cluster smon_scn_to_time ( thread number /* the thread number */ ) / create index smon_scn_to_time_idx on cluster smon_scn_to_time / create table smon_scn_time ( thread number, /* the thread number */ time_mp number, /* time this recent scn represents */ time_dp date, /* time converted into oracle date */ scn_wrp number, /* scn.wrp */ scn_bas number /* scn.bas */ ) cluster smon_scn_to_time (thread) /
复制
观察一下smon_scn_time中的字段项,其中time_mp代表最新的SCN对应的时间。在Oracle 9i中,这个时间取自系统的内核时间,从1970/1/1开始计算。
这样计算主要是源于UNIX中Unix Time的计时方法,Unix Time以秒为点数不断累积来记录时间的变化,时间从UTC 1970年1月1日 00:00:00记起,Unix Time计时方式后来被很多类Unix系统所采用。在这些系统上,Oracle从操作系统获取时间,返回从公元1970年1月1日的UTC时间从0时0分0秒算起到现在所经过的秒数。
time_dp则带表time_mp转换后的系统时间,这个时间是用户可读的。
在MySQL中,存在一个函数可以很容易地将内核时间(也就是此处的time_mp)转换为具体时间,这里暂且借助一下。
从smon_scn_time表中取出当前的最大一条记录(来自Linux下Oracle9204数据库):
SQL> select * from (select * from smon_scn_time order by time_mp desc) where rownum <2; THREAD TIME_MP TIME_DP SCN_WRP SCN_BAS ---------- ---------- ------------------- ---------- ---------- 1 1173610527 2007-03-11 18:55:29 4 1815864065
复制
这里的time_mp值为1173610220,可以通过MySQL中的from_unixtime函数进行转换:
mysql> select from_unixtime(1173610527); +---------------------------+ | from_unixtime(1173610527) | +---------------------------+ | 2007-03-11 18:55:27 | +---------------------------+ 1 row in set (0.00 sec)
复制
可以看到两者的时间吻合得很好。
在Linux下,也可以通过命令很容易地将UNIX时间转换出来:
[root@eygle ~]# date -d '1970-01-01 UTC 1173610527 seconds' +"%Y-%m-%d %T %z" 2007-03-11 18:55:27 +0800
复制
SMON_SCN_TIME表中的另外两个字段scn_wrp和scn_bas加起来带表SCN,scn_wrp带表SCN的高位,scn_bas带表SCN的低位,我们先来取得当前的SCN来验证一下:
SQL> col scn for 99999999999999999 SQL> select dbms_flashback.get_system_change_number scn from dual; SCN ------------------ 18995733321 SQL> select * from (select * from smon_scn_time order by time_mp desc) where rownum <2; THREAD TIME_MP TIME_DP SCN_WRP SCN_BAS ---------- ---------- ------------------- ---------- ---------- 1 1173610527 2007-03-11 18:55:29 4 1815864065
复制
通过scn_wrp=4和scn_bas=1815864065,可以计算出这两者所带表的SCN:
SCN = 4*65536*65536 + 1815864065 = 18995733249
复制
这个记录的SCN和获得的系统SCN相差无几,也就是time_mp/time_dp时间数据库对应的SCN。
通过这几个字段,Oracle将数据库的SCN和时间关联了起来。
SQL> desc smon_scn_time Name Null? Type ----------------------- -------- ---------------- THREAD NUMBER TIME_MP NUMBER TIME_DP DATE SCN_WRP NUMBER SCN_BAS NUMBER
复制
在Oracle 9iR2中,SMON_SCN_TIME一般每5分钟被更新一次。
SQL> select * from v$version where rownum <2; BANNER ---------------------------------------------------------------- Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; Session altered. SQL> select * from smon_scn_time where rownum <10; THREAD TIME_MP TIME_DP SCN_WRP SCN_BAS ---------- ---------- ------------------- ---------- ---------- 1 1173421909 2007-03-09 14:31:51 4 1815762219 1 1173422216 2007-03-09 14:36:58 4 1815762320 1 1173422523 2007-03-09 14:42:05 4 1815762421 1 1173422830 2007-03-09 14:47:13 4 1815762522 1 1173423137 2007-03-09 14:52:20 4 1815762623 1 1173423444 2007-03-09 14:57:27 4 1815762724 1 1173423754 2007-03-09 15:02:34 4 1815762826 1 1173424061 2007-03-09 15:07:41 4 1815762927 1 1173424368 2007-03-09 15:12:49 4 1815763028 9 rows selected.
复制
在Oracle 9iR2中,表属性修改时间和flashback时间差至少应为5分钟,否则就会出现ORA-01466错误,可以通过以下测试来看一下这个特性:
SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; Session altered. SQL> select * from (select * from sys.smon_scn_time order by time_dp desc) where rownum <5; THREAD TIME_MP TIME_DP SCN_WRP SCN_BAS ---------- ---------- ------------------- ---------- ---------- 1 1173492184 2007-03-10 10:03:07 4 1815825067 1 1173491877 2007-03-10 09:57:59 4 1815824966 1 1173491570 2007-03-10 09:52:52 4 1815824865 1 1173491264 2007-03-10 09:47:45 4 1815824764 SQL> select sysdate from dual; SYSDATE ------------------- 2007-03-10 10:05:37 SQL> col scn for 9999999999999999 SQL> variable scn number; SQL> create table eygle (c1 number); Table created. SQL> exec :scn := dbms_flashback.get_system_change_number(); PL/SQL procedure successfully completed. SQL> print scn; SCN ----------------- 18995694319 SQL> insert into eygle values(1); 1 row created. SQL> commit; Commit complete. SQL> exec :scn := dbms_flashback.get_system_change_number(); PL/SQL procedure successfully completed. SQL> print scn; SCN ----------------- 18995694321 SQL> select * from eygle as of scn(:scn); select * from eygle as of scn(:scn) * ERROR at line 1: ORA-01466: unable to read data - table definition has changed
复制
此处等待5分钟,也就是等候SMON_SCN_TIME被记录:
SQL> execute dbms_lock.sleep(300); PL/SQL procedure successfully completed.
复制
此时执行SCN闪回查询可以被支持:
SQL> exec :scn := dbms_flashback.get_system_change_number(); PL/SQL procedure successfully completed. SQL> select * from eygle as of scn(:scn); C1 ---------- 1 SQL> select * from (select * from sys.smon_scn_time order by time_dp desc) where rownum <5; THREAD TIME_MP TIME_DP SCN_WRP SCN_BAS ---------- ---------- ------------------- ---------- ---------- 1 1173492623 2007-03-10 10:10:24 4 1815825230 1 1173492184 2007-03-10 10:03:07 4 1815825067 1 1173491877 2007-03-10 09:57:59 4 1815824966 1 1173491570 2007-03-10 09:52:52 4 1815824865
复制
二、Oracle 10中的 SMON_SCN_TIME
在Oracle 10g中,Oracle有所增强,再来看一下sql.bsq文件中对于smon_scn_time表的定义和说明:
rem create the scn<->time tracking table that smon will maintain rem as a circular queue - notice that we populate the entire rem table with at least 144000 entries (enough for 5 days). rem rem -"thread" is for backward compatibility and is always 0 rem -"orig_thread" is for upgrade/downgrade rem - scn_wrp, scn_bas, and time_dp are for backward compatibility rem and not queried by the ktf layer. rem
复制
注释部分说明了在新版本中smon_scn_time表的变化,现在smon_scn_time表最多能够保存144000记录,也就是说能够支持每3秒一次的数据更新。
在Oracle 10g之前,SMON_SCN_TIME由SMON来获取和记录SCN信息的,从Oracle 10g开始,LGWR首先会在SGA中记录SCN与时间的映射关系(由于LGWR至少每3秒就会被激活一次,所以现在smon_scn_time能够支持大于3秒的闪回),SMON则定期检查SGA是否内存中记录的映射大于磁盘上的,如果有就刷新记录到磁盘。
create cluster smon_scn_to_time ( thread number /* thread, compatibility */ ) / create index smon_scn_to_time_idx on cluster smon_scn_to_time / create table smon_scn_time ( thread number, /* thread, compatibility */ time_mp number, /* time this recent scn represents */ time_dp date, /* time as date, compatibility */ scn_wrp number, /* scn.wrp, compatibility */ scn_bas number, /* scn.bas, compatibility */ num_mappings number, tim_scn_map raw(1200), scn number default 0, /* scn */ orig_thread number default 0 /* for downgrade */ ) cluster smon_scn_to_time (thread) /
复制
注意:在Oracle 10g中,time_mp的时间模式被修改,现在的时间起点和Oracle 9i不再相同。
可以测试一下Oracle10g Flashback时间的限制:
20:41:49 SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; Session altered. 20:41:49 SQL> select sysdate from dual; SYSDATE ------------------- 2007-03-11 20:41:49
复制
创建一个测试表,插入测试数据:
20:41:49 SQL> col scn for 9999999999999999 20:41:49 SQL> variable scn number; 20:41:49 SQL> create table eygle (c1 number); Table created. 20:41:49 SQL> insert into eygle values(1); 1 row created. 20:41:49 SQL> commit; Commit complete.
复制
记录随后的SCN:
20:41:49 SQL> exec :scn := dbms_flashback.get_system_change_number(); PL/SQL procedure successfully completed. 20:41:49 SQL> print scn; SCN ----------------- 18993310233
复制
此时进行闪回查询会出现错误:
20:41:49 SQL> select * from eygle as of scn(:scn); select * from eygle as of scn(:scn) * ERROR at line 1: ORA-01466: unable to read data - table definition has changed
复制
等待3秒后的查询可以获得成功:
20:41:49 SQL> execute dbms_lock.sleep(3); PL/SQL procedure successfully completed. 20:41:52 SQL> select * from eygle as of scn(:scn); C1 ---------- 1 20:41:53 SQL> select sysdate from dual; SYSDATE ------------------- 2007-03-11 20:41:53
复制
当然以上讨论的只是概念和原理上的问题,在实际环境中,很少会遇到对变更时间如此之短的数据表进行恢复的情况。
三、Oracle 11中的 SMON_SCN_TIME
在Oracle 11g中,SMON_SCN_TIME对象有了进一步的演进增强,通过dtxnspc.bsq文件可以获得如下重要信息:
图:dtxnspc.bsq 文件的内容信息
以上信息提示,在props$字典中,增加了一个新的项目“Flashback Timestamp TimeZone”用于记录闪回时间戳的时区信息。
接下来的一点变化是,原来的Cluster更名为smon_scn_to_time_aux,并将存储转移到了SYSAUX表空间中,最后创建的两个索引同样存储在SYSAUX表空间,Oracle不断地将隔离、分离提升性能的思想逐步推进,如下图是smon_scn_time的相关数据结构:
图:11g中smon_scn_time对象结构