暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

SMON_SCN_TIME与闪回查询

原创 eygle 2019-11-28
2231

在闪回恢复操作中,有一个重要的数据结构起着关键性的作用,这个对象就是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文件可以获得如下重要信息:

1.png
图:dtxnspc.bsq 文件的内容信息

以上信息提示,在props$字典中,增加了一个新的项目“Flashback Timestamp TimeZone”用于记录闪回时间戳的时区信息。

接下来的一点变化是,原来的Cluster更名为smon_scn_to_time_aux,并将存储转移到了SYSAUX表空间中,最后创建的两个索引同样存储在SYSAUX表空间,Oracle不断地将隔离、分离提升性能的思想逐步推进,如下图是smon_scn_time的相关数据结构:

2.png
图:11g中smon_scn_time对象结构

最后修改时间:2019-11-28 11:44:18
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论