Oracle DB
服務器 系統時間修改問題 與 SCN 關系的深入研究Oracle DB 服務器 系統時間修改問題 與 SCN 關系的深入研究
論壇里一個朋友說將DB 服務器系統時間往往后修改了3個月(從11年改成10年),啟動DB報600的錯誤。
一. 先做個測試
1.1 關閉DB
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
1.2 修改系統時間
1.2.1 現在時間
[root@singledb ~]# date
Tue Jan 25 11:05:32 EST 2011
1.2.2 修改時間
將時間往前調整一下:
[root@singledb ~]# date -s 1/1/2011
Sat Jan 1 00:00:00 EST 2011
[root@singledb ~]# date
Sat Jan 1 00:00:22 EST 2011
1.3啟動DB
SQL> startup
ORACLE instance started.
Total System Global Area 360710144 bytes
Fixed Size 1219424 bytes
Variable Size 117441696 bytes
Database Buffers 239075328 bytes
Redo Buffers 2973696 bytes
Database mounted.
Database opened.
啟動并沒有問題,也沒有遇到網友的600錯誤。
二. SCN
在這里先對SCN 的知識做下說明。之前的blog里也有說明:
RedoLog Checkpoint 和 SCN關系
http://blog.csdn.net/tianlesoftware/archive/2010/01/24/5251916.aspx
SCN是當Oracle數據更新后,由DBMS自動維護去累積遞增的一個數字。 當一個事務commit時,LGWR會將log buffer寫入redo log file,同時也會將該事務的 SCN同步寫入到redo log file內(wait-until-completed)。因此當你commit transaction時, 在成功的訊息返回之前,LGWR必須先完整的完成上述行為之后,否則你是看不到提交成功的響應訊息。
系統時間標記與scn 之間存在一張表,即SYS 下的SMON_SCN_TIME。
SQL> desc sys.smon_scn_time
名稱 是否為空? 類型
----------------------------------------- -------- ---------------------------
THREAD NUMBER
TIME_MP NUMBER
TIME_DP DATE
SCN_WRP NUMBER
SCN_BAS NUMBER
NUM_MAPPINGS NUMBER
TIM_SCN_MAP RAW(1200)
SCN NUMBER
ORIG_THREAD NUMBER
每隔5 分鐘,系統產生一次系統時間標記與scn 的匹配并存入SYS.SMON_SCN_TIME 表(由SMON 進程來進行Update操作),該表中記錄了最近1440個系統時間標記與scn 的匹配記錄,由于該表只維護了最近的1440 條記錄,即最近5 天內的記錄。
對 系統時間標記與scn 的每5 分鐘匹配一次 做個說明,比如:
SCN:339988 對應 2011-01-25 17:00:00
SCN:339989對應2011-01-25 17:05:00,
當查詢2011-01-25 17:00:00 到2011-01-25 17:04:59 這段時間點內的SCN時,oracle 都會將其匹配為SCN:339988。
查看SCN 和 timestamp 之間的對應關系:
select scn,to_char(time_dp,'yyyy-mm-dd hh34:mi:ss') from sys.smon_scn_time order by 2;
查詢目前系統最新的SCN:
select dbms_flashback.get_system_change_number from dual;
timestamp 與 SCN 互換的2個方法:
select timestamp_to_scn(to_date('2011-01-25 12:10:00','yyyy-mm-dd hh34:mi:ss')) from dual;
select scn_to_timestamp(351277605) from dual;
在Metalink 搜到一篇Timestamp與SCN 之間映射關系的文章,原文參考:
How to map SCN with Timestamp before 10g [ID 365536.1]
http://blog.csdn.net/tianlesoftware/archive/2011/01/25/6163757.aspx
However, in earlier releases, while there is a system object - SYS.SMON_SCN_TIME that will provide the SCN to TIME mapping information. There is no conversion facility provided.
SYS.SMON_SCN_TIME will have a maximum of 1440 rows and each record will be for a 5 minute period. Oracle maintains this information for maximum of 5 days after which the records will be recycled.
This means that data is stored 12 times per hour * 24 hours * 5 days=1440 rows.
SCN value is stored internally as :
i. SCN_wrap
ii. SCN_base
Whenever the SCN is incremented, the BASE component is incremented first unil it reaches it maximum. Once the BASE reaches the maximum value allowed, it is initialized to zero again after incrementing the WRAP by 1.
--開始時WRAP 為0,即SCN_WRP=0. 當BASE增長到最大值后,SCN_BAS變為0. 同時SCN_WRP 增長到1
Using this logic, we can calculate the timestamp of the SCN as follows:
(SCN_WRP * 4294967296) + SCN_BAS should give us the SCN in the number format
--SCN 的計算公式,這里我們看出SCN 是根據SCN_BAS計算出來的。和系統時間是沒有關系的。 只是方便我們來進行操作,如閃回恢復,而將SCN 和 系統時間每隔5分鐘進行一次映射。 映射完后,由SMON進程將映射關系寫入SMON_SCN_TIME表。 可以把SCN 看成是Oracle 內部的時間。
To get the time/date for an SCN value in 9i, use the following example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- (a) Get the current SCN base.
SQL>select max(scn_bas) SCN_BASE from smon_scn_time;
1603342197
--(b) Get the complete SCN and the timestamp.
SQL> alter session set NLS_DATE_FORMAT='DD-MON-YY HH:MI:SS';
SQL> select time_dp TIMESTAMP, scn_wrp*4294967296+SCN_bas SCN from smon_scn_time where scn_bas='1603342197';
TIMESTAMP SCN
------------------ ----------
28-JUL-06 05:31:08 8252235517813
這篇文章將的是10g之前的。 在metalink上搜了半天,只找到一條區別,就是11g下smon_scn_time 存儲的記錄可以超過1440條。
在這里插一句,10g的g 表示Grid.
三. 修改系統時間與SCN 關系
通過前面2節的分析,可以看出修改系統時間和SCN沒有直接的關系。在第一節中也做了測試。 對于DB 服務器如何修改時間:
(1)停止應用
(2)停止數據庫
(3)修改時間
不過,如果不是特殊情況,不建議修改。穩定第一。尤其是RAC 環境,對時間要求更嚴格。
在這里說了,修改系統時間和SCN 沒有直接影響,但我在測試中,發現他們是有聯系的。
SQL> alter session set nls_date_format='yyyy-mm-dd hh34:mi:ss';
Session altered.
SQL> select sysdate from dual;
SYSDATE
-------------------
2010-01-01 01:21:58
SQL> select * from ( select scn,to_char(time_dp,'yyyy-mm-dd hh34:mi:ss') time from sys.smon_scn_time order by 2 desc) where rownum<10;
SCN TIME
---------- -------------------
943223 2012-01-01 02:43:33
943058 2012-01-01 02:38:14
942811 2012-01-01 02:33:02
922652 2012-01-01 00:01:52
922182 2012-01-01 00:00:45
920862 2011-01-25 11:46:21
920717 2011-01-25 11:41:17
920571 2011-01-25 11:36:38
919996 2011-01-25 11:31:21
9 rows selected.
SQL> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
945394
從上面的幾個查詢結果我們發現二個問題:
(1)smon_scn_time表中的SCN(943223) 小于系統當前的SCN值(945394)。
(2)smon_scn_time表中最后更新時間(2012-01-01 02:43:33)大于系統當前時間(2010-01-01 01:21:58)。
由此我們推出一個結論:當smon_scn_time最后更新時間大于系統時間時,SMON不會將SCN 與TIMESTAMP的映射結果寫入到sys.smon_scn_time表中。
如果SCN 與TIMESTAMP的映射不能寫入到smon_scn_time表中,我們就不能進行SCN 與TIMESTAMP轉換,就不能利用timestamp進行相關的操作,如恢復。參考:
Oracle 不同故障的恢復方案
http://blog.csdn.net/tianlesoftware/archive/2010/12/30/6106178.aspx
為了驗證上面的結論,我們把系統改成大于smon_scn_time最后更新時間。
SQL> select sysdate from dual;
SYSDATE
-------------------
2012-02-01 00:00:16
SQL> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
946462
SQL> select * from ( select scn,to_char(time_dp,'yyyy-mm-dd hh34:mi:ss') time from sys.smon_scn_time order by 2 desc) where rownum<10;
SCN TIME
---------- -------------------
945872 2012-02-01 00:00:51
943223 2012-01-01 02:43:33
943058 2012-01-01 02:38:14
942811 2012-01-01 02:33:02
922652 2012-01-01 00:01:52
922182 2012-01-01 00:00:45
920862 2011-01-25 11:46:21
920717 2011-01-25 11:41:17
920571 2011-01-25 11:36:38
這次成功寫入smon_scn_time表。由此可見:
(1)系統時間往后改(如從2011年改到2012年),對DB 沒有影響,可能某些應用可能會受影響。
(2)系統時間往前改(如從2011年改到2010年),那么SMON 不會將SCN 與timestamp寫入smon_scn_time表。
所以對于生產系統,在系統上線之前就應該校隊好系統時間,本著穩定第一的原則,上線之后就不建議在修改DB 服務器的時間了。