不那麼靠譜的ORA_ROWSCN

zhang41082發表於2019-06-08


很多人都知道ORA_ROWSCN可以表示表中某行記錄的SCN號,從這個SCN號可以大致推算出這行記錄最後修改的時間是什麼時候。不過建立表的時候如 果沒有顯示的指定表的rowdependencies屬性,那麼這裡的ORA_ROWSCN就是指這個BLOCK中的所有行的最後修改時間,這個時候使用 ORA_ROWSCN來判斷單條記錄的修改時間就不那麼靠譜了(不過標題裡的不靠譜並不是指這個),但如果應用比較特殊,比如是記錄日誌,表中資料不停的 插入,沒有更改和刪除,那這個ORA_ROWSCN還是可以基本判斷每條記錄的大致插入時間的,所以這要具體情況具體分析,不能一棍子打死。其實未顯示設 置rowdependencies屬性的時候,ORA_ROWSCN就是取的BLOCK的SCN,所以就會出現BLOCK中所有記錄都是同一個 ORA_ROWSCN。這裡的不靠譜講的是另外一個故事。。。[@more@]

LINUX平臺的ORACLE 10G,業務表是壓縮表,存放歷史資料,每天凌晨把歷史資料匯入進來,大概整個過程不到20分鐘,資料進來後不會做更新、刪除等操作。可是凌晨執行的業務無意中卻發現透過ORA_ROWSCN轉換出來的時間卻是下午的時間,嚇了一跳,難道凌晨跑完下午還有人更改過這些資料?這不符合業務邏輯的,於是開始深入的查詢。

首先來看看這個表裡的資料的資訊(拿一條資料為例):
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM HISTORY --as of scn 14744229299
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 17:05:45 1474422929 AAA+wtABKAABJCfABW 74 299167

這裡可以看到,凌晨插入的資料,最後更新時間卻變成了下午5點多,而這個表的資料在下午是不會有變化的,更不要說具體到某個BLOCK了,那麼就DUMP這個BLOCK出來看看,結果如下:
Start dump data blocks tsn: 35 file#: 74 minblk 299167 maxblk 299167
buffer tsn: 35 rdba: 0x1284909f (74/299167)
scn: 0x0003.6ed70907 seq: 0x01 flg: 0x04 tail: 0x09070601
frmt: 0x02 chkval: 0x1062 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1

這裡可以看到BLOCK的SCN為:0x0003.6ed70907,轉換成十進位制為:14744488199,跟我們查詢出來的ORA_ROWSCN對不上,這個SCN轉換成時間為:2009-12-23 17:12:59,這就怪異了!!難道中間誰改過這個BLOCK了,立馬重新執行上面的查詢:
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM HISTORY --as of scn 14744229299
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 17:05:45 1474422929 AAA+wtABKAABJCfABW 74 299167

可以看到結果一摸一樣,說明ORA_ROWSCN查出來的並不是BLOCK的SCN,而且BLOCK的SCN怎麼也會變成下午的SCN號呢?更奇怪的還在下面,我想看看這個表凌晨時候是什麼情況,所以使用FLASHBACK QUERY的特性來查檢視,先搞個大概的早上的時候的SCN:
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(14730288199), 'yyyy-mm-dd hh24:mi:ss')
2 FROM DUAL;

TO_CHAR(SCN_TO_TIMESTAMP(14730
------------------------------
2009-12-23 09:58:32

那麼使用14730288199去FLASHBACK QUERY:
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM HISTORY AS OF SCN 14730288199
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 09:58:32 1473028808 AAA+wtABKAABJCfABW 74 299167

這個時候的ORA_ROWSCN往前跳了,跳到了FLASHBACK附近的SCN,但還是不一樣的,把SCN再往前挪一點看看:
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM HISTORY AS OF SCN 14730008199
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 09:48:30 1473000743 AAA+wtABKAABJCfABW 74 299167

還是一樣的情況,那麼把這個BLOCK重新DUMP出來看看:
Start dump data blocks tsn: 35 file#: 74 minblk 299167 maxblk 299167
buffer tsn: 35 rdba: 0x1284909f (74/299167)
scn: 0x0003.6ee25256 seq: 0x01 flg: 0x04 tail: 0x52560601
frmt: 0x02 chkval: 0x4e2f type: 0x06=trans data
Hex dump of block: st=0, typ_found=1

BLOCK的SCN為:0x0003.6ee25256,轉換成十進位制是:14745227862,然後轉換成時間:2009-12-23 17:38:37,還是到處對不上。這時我在執行原先的查詢(不FLASHBACK):
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM HISTORY --AS OF SCN 14730008199
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 09:48:30 1473000743 AAA+wtABKAABJCfABW 74 299167

可以驚奇的看到ORA_ROWSCN固定在之前FLASHBACK QUERY的時候了,完全亂套了,所以這才是標題所指的ORA_ROWSCN不靠譜的原因。相同的測試,在普通表上沒有這樣的問題,難道是壓縮表每次訪問的時候進行了一次解壓?

無意中發現ITL那邊有點線索:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x004c.012.000a33b2 0x00000000.0000.00 C-U- 0 scn 0x0003.6dfa1386

其中的SCN 0x0003.6dfa1386轉換成十進位制:14730007430,居然正好是ORA_ROWSCN的值,而且FLASHBACK的SCN如果往後跳的話,這個值不變,查詢出來的ORA_ROWSCN也不變,並且重新開其他的SESSION查詢結果也一樣。但是當FLASHBACK QUERY的SCN往前跳的時候,這裡的SCN也跟著往前跳,並且等於ITL這裡顯示的SCN(不是每跳一個SCN都會改變這裡的ORA_ROWSCN,是要跳一段,至於這個段是多大?疑惑中)。然後當你乾脆跳到這條記錄插入之前的SCN,這時候查詢記錄結果為空,然後再把SCN往後挪,挪到記錄插入之後的時間,之後再顯示的SCN就是記錄真正插入的時候的SCN了:
SQL> SELECT TO_CHAR(SCN_TO_TIMESTAMP(ORA_ROWSCN), 'yyyy-mm-dd hh24:mi:ss') TM,
2 ORA_ROWSCN,
3 ROWID,
4 DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) FID,
5 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BID
6 FROM SEASHELL.BALANCE_HISTORY AS OF SCN 14730000000
7 WHERE ROWID = 'AAA+wtABKAABJCfABW';

TM ORA_ROWSCN ROWID FID BID
------------------- ---------- ------------------ ---------- ----------
2009-12-23 07:51:55 1472795708 AAA+wtABKAABJCfABW 74 299167

凌晨7點多,這個時間才是記錄真正進來的時間!

那麼,疑惑還沒有解開,還得來看看ITL這裡顯示的SCN/FSC是何方神聖。這裡的SCN和FSC其實就是這個ITL對應的事務提交時候的SCN,那麼這裡所有槽位上的最大的一個SCN號就表示這個BLOCK最後被更新的時候的SCN。之所以使用SCN和FSC來區分開來,是因為這裡還涉及到一個延遲塊清除的問題。如果是延遲塊清除,那這裡就顯示的是SCN;如果沒有延遲的,那這裡顯示的就是FSN。

又來回折騰了幾次,發現壓縮表每次都很不靠譜,沒思路了,不過發現在FLASHBACK的時候,有時候ORA_ROWSCN顯示的並不是BLOCK HEADER的SCN而是ITL這裡的SCN。

總之,對於壓縮表的ORA_ROWSCN使用要小心,不知道是不是因為查詢的時候要解壓,然後解壓的時候也去更新這個SCN?





UPDATE:
瞭解到延遲塊清除後,難道這些BLOCK是因為這個原因?還沒有COMMIT的時候,BLOCK已經寫入磁碟了,然後到查詢之前一直沒有人來讀取過,所以在讀取的時候會清除BLOCK上的事務的資訊,那麼BLOCK_HEADER的SCN就變成了ITL上的事務對應的UNDO段的CONTROL SCN?


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25016/viewspace-1030149/,如需轉載,請註明出處,否則將追究法律責任。

相關文章