使用10046跟蹤Oracle前映象資料讀

realkid4發表於2013-02-18

Undo前映象”是Oracle早期推出的特性之一,也是在諸多資料庫產品中異軍突起的致勝法寶。當我們在一個會話中啟動事務,對資料進行增加、修改和刪除操作的時候,只要沒有提交事務,其他會話只能看到資料的舊版本,也就是事務會話修改之前的版本。所以,在Oracle中,select操作不會阻塞任何操作,也不會被任何操作所阻塞。

 

 

1Undo與前映象

 

這樣的特性就是依賴Oracle推出的Undo前映象機制。當我們開啟事務,修改一個資料塊的時候,Oracle首先會修改資料塊塊頭的ITL(事務槽)資訊,將當前事務資訊(xid事務標識)寫入到ITL中的一行。之後標記下這個事務對應的Undo空間地址。之後,才能進行資料塊的修改。

 

在修改資料塊的過程中,OracleServer Process會將資料塊的原有內容(對UpdateDelete操作而言),儲存到Undo表空間上Undo段的位置上。

 

Undo段內容有很多的用途。當另外的會話需要訪問資料塊時,首先會去檢查資料塊的ITL事務槽資訊,檢視要訪問的資料塊是不是正在被修改。如果正在被修改,就根據ITL上面留下的事務槽資訊訪問Undo段。同時,如果只有一部分資料被修改,Oracle Server Process還要結合資料塊中未被修改的內容進行結果集合拼裝。

 

只有在事務正式完成,commit或者rollback之後,Undo段中的extent狀態才不再是Active。非Active狀態的Undo也有其價值,Oracle的“多版本一致讀”、“Flashback”等特性,都是基於對非Active狀態Undo的資料利用。

 

本篇,我們打算使用10046等待事件,監控一下Oracle是如何進行前映象讀取動作的。

 

2、實驗環境準備

 

我們選擇11gR2版本進行試驗。

 

 

SQL> select * from v$version;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

PL/SQL Release 11.2.0.1.0 - Production

CORE        11.2.0.1.0         Production

TNS for 32-bit Windows: Version 11.2.0.1.0 - Production

NLSRTL Version 11.2.0.1.0 – Production

 

 

建立實驗資料表T

 

 

SQL> create table t as select * from dba_objects;

Table created

 

SQL> select object_id from dba_objects where wner='SYS' and object_name='T';

 OBJECT_ID

----------

188217

 

SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where segment_name='T' and wner='SYS';

HEADER_FILE HEADER_BLOCK      BYTES     BLOCKS    EXTENTS

----------- ------------ ---------- ---------- ----------

          1        89344   10485760       1280         25

 

 

對應分割槽資訊。

 

 

SQL> select extent_id, file_id, block_id, blocks from dba_extents where segment_name='T' and wner='SYS';

 

 EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS

---------- ---------- ---------- ----------

         0          1      89344          8

         1          1      89352          8

         2          1      89384          8

(篇幅原因,省略部分內容……

        22          1      94592        128

        23          1      94720        128

        24          1      95616        128

25 rows selected

 

 

系統當前採用自動化Undo管理。

 

 

SQL> show parameter undo;

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

undo_management                      string      AUTO

undo_retention                       integer     900

undo_tablespace                      string      UNDOTBS1

 

 

3Trace檔案獲取

 

我們現在一個會話中,開啟事務刪除所有的資料。

 

 

SQL> select sid from v$mystat where rownum<2;

       SID

----------

       145

 

SQL> delete t;

84331 rows deleted

 

 

在另一個會話中,我們啟動實驗。注意,首選需要排除快取髒塊的影響,將髒塊寫入到資料檔案。

 

 

--另一個會話

SQL> select sid from v$mystat where rownum<2;

       SID

----------

        21

 

SQL> alter system checkpoint;

System altered

 

SQL> alter system flush shared_pool;

System altered

 

SQL> alter system flush buffer_cache;

System altered

 

SQL> select value from v$diag_info where name='Default Trace File';

VALUE

------------------------------------------------------------------------------

d:\app\bspdev\diag\rdbms\ora11gw\ora11gw\trace\ora11gw_ora_2208.trc

 

 

開啟跟蹤操作過程。

 

 

SQL> alter session set events '10046 trace name context forever, level 12';

會話已更改。

 

SQL> select count(*) from t;

  COUNT(*)

----------

     84331

 

SQL> alter session set events '10046 trace name context off';

會話已更改。

 

 

我們可以在指定目錄上找到對一個trace檔案ora11gw_ora_2208.trc

 

4、結果分析

 

我們從trace原始檔案中,發現了比普通FTSFull Table Scan)操作的不同。更多的操作細節和資源消耗。從直觀上感覺,進行select操作消耗更多的時間。

 

Trace檔案細節資訊。

 

 

=====================

PARSING IN CURSOR #3 len=22 dep=0 uid=0 ct=3 lid=0 tim=156068168118 hv=2763161912 ad='30303208' sqlid='cyzznbykb509s'

select count(*) from t

END OF STMT

PARSE #3:c=234375,e=1924863,p=525,cr=4754,cu=0,mis=1,r=0,dep=0,og=1,plh=2966233522,tim=156068168115

EXEC #3:c=0,e=43,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2966233522,tim=156068168297

WAIT #3: nam='SQL*Net message to client' ela= 3 driver id=1413697536 #bytes=1 p3=0 obj#=188217 tim=156068168350

WAIT #3: nam='db file scattered read' ela= 14472 file#=1 block#=89345 blocks=3 obj#=188217 tim=156068182967

WAIT #3: nam='db file sequential read' ela= 14293 file#=3 block#=7522 blocks=1 obj#=0 tim=156068197389

WAIT #3: nam='db file sequential read' ela= 2580 file#=3 block#=7521 blocks=1 obj#=0 tim=156068200612

WAIT #3: nam='db file sequential read' ela= 2353 file#=3 block#=7520 blocks=1 obj#=0 tim=156068204231

WAIT #3: nam='db file sequential read' ela= 2355 file#=3 block#=7524 blocks=1 obj#=0 tim=156068207835

WAIT #3: nam='db file sequential read' ela= 2353 file#=3 block#=7523 blocks=1 obj#=0 tim=156068211050

WAIT #3: nam='db file sequential read' ela= 2352 file#=3 block#=7526 blocks=1 obj#=0 tim=156068215311

(省略部分內容……

FETCH #3:c=2265625,e=11162122,p=3131,cr=87951,cu=0,mis=0,r=1,dep=0,og=1,plh=2966233522,tim=156079330523

STAT #3 id=1 cnt=1 pid=0 pos=1 bj=0 p='SORT AGGREGATE (cr=87951 pr=3131 pw=0 time=0 us)'

STAT #3 id=2 cnt=84331 pid=1 pos=1 bj=188217 p='TABLE ACCESS FULL T (cr=87951 pr=3131 pw=0 time=21345344 us cost=329 size=0 card=86741)'

WAIT #3: nam='SQL*Net message from client' ela= 785 driver id=1413697536 #bytes=1 p3=0 obj#=0 tim=156079331506

FETCH #3:c=0,e=3,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=2966233522,tim=156079331569

WAIT #3: nam='SQL*Net message to client' ela= 3 driver id=1413697536 #bytes=1 p3=0 obj#=0 tim=156079331615

 

*** 2013-02-18 08:20:42.187

WAIT #3: nam='SQL*Net message from client' ela= 43041122 driver id=1413697536 #bytes=1 p3=0 obj#=0 tim=156122372771

CLOSE #3:c=0,e=26,dep=0,type=0,tim=156122372940

=====================

 

 

系統操作消耗最大的部分,在於對資料塊的讀操作等待。從SQL的情況來看,Oracle只有全表掃描一條道路可以選擇。而Oracle進行FTS操作的過程我們是清晰的。

 

在之前的文章中,我們討論過FTS的操作過程。Oracle在進行FTS的時候,首先會從資料字典中定位資料頭塊位置,並且以單塊讀方式獲取到頭塊。在頭塊分析中,Server Process獲取到了資料段所有extents的分割槽資訊,包括起始頭塊位置和塊數量。之後根據這些資訊進行一系列的多塊讀操作。

 

但是從我們看到的raw結果,Oracle在反覆進行對file_id=1file_id=3檔案塊的讀取。對1號檔案的讀取主要是多塊讀,但是大小遠遠小於我們的經驗值。對3號檔案完全是點讀動作,一次只會讀取一個資料塊。

 

下面我們擷取出一個片段進行分析。

 

 

WAIT #3: nam='db file scattered read' ela= 14472 file#=1 block#=89345 blocks=3 obj#=188217 tim=156068182967

WAIT #3: nam='db file sequential read' ela= 14293 file#=3 block#=7522 blocks=1 obj#=0 tim=156068197389

WAIT #3: nam='db file sequential read' ela= 2580 file#=3 block#=7521 blocks=1 obj#=0 tim=156068200612

WAIT #3: nam='db file sequential read' ela= 2353 file#=3 block#=7520 blocks=1 obj#=0 tim=156068204231

WAIT #3: nam='db file sequential read' ela= 2355 file#=3 block#=7524 blocks=1 obj#=0 tim=156068207835

WAIT #3: nam='db file sequential read' ela= 2353 file#=3 block#=7523 blocks=1 obj#=0 tim=156068211050

WAIT #3: nam='db file sequential read' ela= 2352 file#=3 block#=7526 blocks=1 obj#=0 tim=156068215311

WAIT #3: nam='db file sequential read' ela= 2467 file#=3 block#=7525 blocks=1 obj#=0 tim=156068218786

WAIT #3: nam='db file scattered read' ela= 6824 file#=1 block#=89349 blocks=3 obj#=188217 tim=156068229398

WAIT #3: nam='db file sequential read' ela= 2356 file#=3 block#=7531 blocks=1 obj#=0 tim=156068231844

WAIT #3: nam='db file sequential read' ela= 2457 file#=3 block#=7530 blocks=1 obj#=0 tim=156068234728

 

 

從標紅部分可以看到,Oracle首先到1號檔案讀取了3個資料塊,啟示塊號是89345。這個塊是什麼呢?

 

 

SQL> select owner, segment_name, EXTENT_ID, FILE_ID, BLOCK_ID, BLOCKS from dba_extents where file_id=1 and block_id<=89345 and block_id+blocks-1>89345;

 

OWNER      SEGMENT_NAME          EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS

---------- -------------------- ---------- ---------- ---------- ----------

SYS        T                             0          1      89344          8

 

 

顯然,這三個塊是資料表T第一個extent對應的分割槽資料塊。Oracle在讀取了之後,從ITL上知道了對應的Undo空間地址,就連續進行了7次對file_id=3的塊操作,而且每次都是一個資料塊。

 

我們隨機找一下一個塊的屬性。

 

 

SQL> select owner, segment_name, extent_id, file_id, block_id, blocks, status from dba_undo_extents where file_id=3 and block_id<=7522 and block_id+blocks-1>7522;

 

OWNER      SEGMENT_NAME          EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS STATUS

---------- -------------------- ---------- ---------- ---------- ---------- ---------

SYS        _SYSSMU8_1557854099$         21          3       7424        128 ACTIVE

 

 

很明顯,Oracle在讀了三個資料塊之後,非連續(隨機)的訪問了Undo_SYSSMU8_1557854099$的第21分割槽中的一些資料塊。目的很明顯,就是獲取前映象,而且這些前映象是進行單塊讀操作。

 

當完成所有的前映象點塊讀之後,Oracle就可以合併出一致讀的結果集。之後,在進行下3個塊的讀取動作。

 

5、結論

 

從上面的實驗裡面,我們可以得到幾個想法。

 

首先,Oracle進行一致讀的時候,每次進行多塊讀,獲取的資料塊數量是相對較少的。一般來說,多塊讀的上限規模和作業系統IO能力、Oracle內部多塊讀引數有關。但是在例項中,一次只會讀出三塊,猜想與一致讀有關。

 

其次,在獲取到資料塊之後,Oracle會按照每個塊中ITL資訊,以單塊的方式“點讀”那些Undo塊。並且進行合併結果集操作。

 

最後,從10046結構看,一致讀是相當消耗資源的。在一致讀狀態下,檢索效能要受到幾個數量級別的影響。

 

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

相關文章