Oracle比其他數據庫牛逼的地方好幾個,其中一個很重要的就是undo表空間的引入(當然,鎖也是很牛逼的一個東西)
1.oracle段的類型:
SQL> select segment_type from dba_segments t group by t.segment_type; SEGMENT_TYPE ------------------ LOBINDEX INDEX PARTITION TABLE PARTITION NESTED TABLE ROLLBACK LOB PARTITION LOBSEGMENT INDEX TABLE CLUSTER TYPE2 UNDO
在dba_tablespace中.表空間的類型分為:undo,temporary,permanent
2.查看undo表空間創建之后創建的段
select * from dba_segments where tablespace_name = 'UNDOTBS1';
每一個undo段至少要有2個extent
也可以查看
select * from dba_rollback_segs
在status一欄有顯示從數據庫啟動用的有10個undo段,如果存在多個undo表空間,那么從status=online的可以查看當前在用的回滾段.
個人認為9i的最大貢獻就是,開始undo可以自動管理
3.每個回滾段最多有幾個事務數?
在9i以前通過參數transactions_per_rollback_segment(默認是5個),現在已經失效,從10g開始默認一個回滾段上只有一個事務,如果回滾段不夠的話,那么就自己創建undo段,直到undo表空間用完.這個時候,回滾段上的事務才開始有多個.
4.最牛逼的一致性讀
一致性讀(Consistent Get)是Oracle一個非常優秀的特性.(當然它也是產生ora-1555錯誤的主要原因)
在標准SQL中,為了防止並發事務中產生臟讀,就需要通過加鎖來控制.這樣就會帶來死鎖、阻塞的問題,即時是粒度最小的行級鎖,也無法避免這些問題.
為了解決這一矛盾,Oracle充分利用的回歸段,通過會滾段進行一致性讀取,即避免了臟讀,又大大減少了系統的阻塞、死鎖問題.
Oracle是如何實現一致性讀的:
當Oracle更新數據塊(Data Block Oracle中最小的存儲單位)時,會在兩個地方記錄下這一更新動作.一個是在Redo Segment,.一個是回滾段UNDO Segment.並在數據塊頭部標示出來是否有修改數據.一個語句在讀取數據快時,如果發現這個數據塊是在它讀取的過程中被修改的(即開始執行讀操作時並沒有被修改),就不直接從數據塊上讀取數據,而是從相應的回滾段條目中讀取數據.這就保證了最終結果應該是讀操作開始時的那一時刻的快照(snapshot),而不會受到讀期間其他事務的影響.這就是Oracle的一致性讀,也可以叫做多版本(Multi-Versioning).
5.ORACLE的據庫事務隔離級別
事務隔離級別:一個事務對數據庫的修改與並行的另一個事務的隔離程度。
兩個並發事務同時訪問數據庫表相同的行時,可能存在以下三個問題:
(1)幻想讀:事務T1讀取一條指定where條件的語句,返回結果集。此時事務T2插入一行新記錄,恰好滿足T1的where條件。然后T1使用相同的條件再次查詢,結果集中可以看到T2插入的記錄,這條新紀錄就是幻想。
(2)不可重復讀取:事務T1讀取一行記錄,緊接着事務T2修改了T1剛剛讀取的記錄,然后T1再次查詢,發現與第一次讀取的記錄不同,這稱為不可重復讀。
(3)臟讀:事務T1更新了一行記錄,還未提交所做的修改,這個T2讀取了更新后的數據,然后T1執行回滾操作,取消剛才的修改,所以T2所讀取的行就無效,也就是臟數據。
為了處理這些問題,SQL標准定義了以下幾種事務隔離級別
SET TRANSACTION ISOLATION LEVEL
[READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
Oracle數據庫支持READ COMMITTED 和 SERIALIZABLE這兩種事務隔離級別。Oracle不支持臟讀。
6.Oracle是怎樣實現一致性讀的,我們可以通過以下實驗來查看
(1)建一個測試的表
SQL> create tablespace test datafile '/u01/app/oracle/oradata/pmisdb/test.dbf' size 20M; Tablespace created. SQL> create table tt (id int,name varchar2(10)) tablespace test; Table created. SQL> SQL> insert into tt values(1,'a++'); 1 row created. SQL> insert into tt values(2,'b'); 1 row created. SQL> insert into tt values(3,'c'); 1 row created. SQL> commit; Commit complete. SQL> SQL> select * from tt; ID NAME ---------- ---------- 1 a++ 2 b 3 c SQL>
(2).打開一個session A ,對改表進行update操作
session A >update tt set name='a' where id=1; 1 row updated.
session A >select * from tt; ID NAME ---------- ---------- 1 a 2 b 3 c
(3)打開一個session B ,進行查詢..因為有一致性讀的特性,所以在session B中,在A沒有提交前,B是看不到A修改的數據的.
session B >select * from tt; ID NAME ---------- ---------- 1 a++ 2 b 3 c session B >
(4)可以根據rowid以及oracle提供的dbms_rowid包來查看該條記錄所在的數據文件和數據塊
session A >select id,name,rowid from tt; ID NAME ROWID ---------- ---------- ------------------ 1 a AAARFuAAIAAAAAQAAA 2 b AAARFuAAIAAAAAQAAB 3 c AAARFuAAIAAAAAQAAC SQL> select dbms_rowid.rowid_relative_fno('AAARFuAAIAAAAAQAAA') as file#, 2 dbms_rowid.rowid_block_number('AAARFuAAIAAAAAQAAA') as block# 3 from dual; FILE# BLOCK# ---------- ---------- 8 16
(5)根據查詢到的文件號和塊號進行dump,注意這個地方dump的其實是內存里面的數據,如果需要dump磁盤上的數據文件,那么把8改成具體的路徑就可以了,因為oracle寫是異步的,這個時候磁盤的數據文件並不一定已經有這個信息了.
session A >alter system dump datafile 8 block 16; System altered. session A >SELECT d.VALUE || '/' || LOWER(RTRIM(i.INSTANCE, CHR(0))) || '_ora_' || 2 p.spid || '.trc' trace_file_name 3 FROM (SELECT p.spid 4 FROM v$mystat m, v$session s, v$process p 5 WHERE m.statistic# = 1 6 AND s.SID = m.SID 7 AND p.addr = s.paddr) p, 8 (SELECT t.INSTANCE 9 FROM v$thread t, v$parameter v 10 WHERE v.NAME = 'thread' 11 AND (v.VALUE = 0 OR t.thread# = TO_NUMBER(v.VALUE))) i, 12 (SELECT VALUE FROM v$parameter WHERE NAME = 'user_dump_dest') d; TRACE_FILE_NAME -------------------------------------------------------------------------------- /u01/app/oracle/admin/pmisdb/udump/pmisdb_ora_3827.trc
(6)打開trace文件,進行觀察,分別截取開頭和有關事務的內容:
[root@pmiscs ~]# more /u01/app/oracle/admin/pmisdb/udump/pmisdb_ora_3790.trc Dump file /u01/app/oracle/admin/pmisdb/udump/pmisdb_ora_3790.trc Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production With the Partitioning, OLAP, Data Mining and Real Application Testing options ORACLE_HOME = /u01/app/oracle/product/10.2.0/db_1 System name: Linux Node name: pmiscs Release: 2.6.18-92.el5 Version: #1 SMP Tue Apr 29 13:16:12 EDT 2008 Machine: i686 Instance name: pmisdb Redo thread mounted by this instance: 1 Oracle process number: 13 Unix process pid: 3790, image: oracle@pmiscs (TNS V1-V3) *** ACTION NAME:() 2012-09-25 13:19:01.211 *** MODULE NAME:(sqlplus@pmiscs (TNS V1-V3)) 2012-09-25 13:19:01.211 *** SERVICE NAME:() 2012-09-25 13:19:01.211 *** SESSION ID:(544.3) 2012-09-25 13:19:01.211 Successfully allocated 2 recovery slaves Using 543 overflow buffers per recovery slave Thread 1 checkpoint: logseq 613, block 2, scn 18284996 cache-low rba: logseq 613, block 449 on-disk rba: logseq 613, block 508, scn 18285712 change track rba: logseq 613, block 507, scn 18285711 start recovery at logseq 613, block 449, scn 0 ----- Redo read statistics for thread 1 ----- Read rate (ASYNC): 29Kb in 0.19s => 0.15 Mb/sec Total physical reads: 4096Kb Longest record: 2Kb, moves: 0/11 (0%) Longest LWN: 6Kb, moves: 0/47 (0%), moved: 0Mb Last redo scn: 0x0000.0117048f (18285711) ---------------------------------------------- ----- Recovery Hash Table Statistics --------- Hash table buckets = 32768 Longest hash chain = 1 Average hash chain = 9/9 = 1.0 Max compares per lookup = 1 Avg compares per lookup = 12/21 = 0.6 ---------------------------------------------- *** 2012-09-25 13:19:01.442 KCRA: start recovery claims for 9 data blocks *** 2012-09-25 13:19:01.499 KCRA: blocks processed = 9/9, claimed = 9, eliminated = 0 *** 2012-09-25 13:19:01.499 Recovery of Online Redo Log: Thread 1 Group 6 Seq 613 Reading mem 0 ----- Recovery Hash Table Statistics --------- Hash table buckets = 32768 Longest hash chain = 1 Average hash chain = 9/9 = 1.0 Max compares per lookup = 1 Avg compares per lookup = 21/21 = 1.0 ---------------------------------------------- kwqmnich: current time:: 5: 19: 6 kwqmnich: instance no 0 check_only flag 1 kwqmnich: initialized job cache structure *** 2012-09-25 13:21:45.194 Start dump data blocks tsn: 10 file#: 8 minblk 16 maxblk 16 buffer tsn: 10 rdba: 0x02000010 (8/16) scn: 0x0000.01175499 seq: 0x01 flg: 0x00 tail: 0x54990601 frmt: 0x02 chkval: 0x0000 type: 0x06=trans data Hex dump of block: st=0, typ_found=1 Dump of memory from 0x0DCC2400 to 0x0DCC4400 ... Block header dump: 0x02000010 Object id on Block? Y seg/obj: 0x1116e csc: 0x00.1175499 itc: 2 flg: E typ: 1 - DATA brn: 0 bdba: 0x2000009 ver: 0x01 opc: 0 inc: 0 exflg: 0 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0004.005.00000c41 0x00800c4a.0692.13 ---- 1 fsc 0x0001.00000000 0x02 0x0003.01b.00000cfe 0x008006d1.072a.04 C--- 0 scn 0x0000.01151109 data_block_dump,data header at 0xdcc2464 =============== tsiz: 0x1f98 hsiz: 0x18 pbl: 0x0dcc2464 bdba: 0x02000010 76543210 flag=-------- ntab=1 nrow=3 frre=-1 fsbo=0x18 fseo=0x1f5c avsp=0x1f64 tosp=0x1f65 0xe:pti[0] nrow=3 offs=0 0x12:pri[0] offs=0x1f5c 0x14:pri[1] offs=0x1f88 0x16:pri[2] offs=0x1f80 block_row_dump: tab 0, row 0, @0x1f5c tl: 8 fb: --H-FL-- lb: 0x1 cc: 2 col 0: [ 2] c1 02 col 1: [ 1] 61 tab 0, row 1, @0x1f88 tl: 8 fb: --H-FL-- lb: 0x0 cc: 2 col 0: [ 2] c1 03 col 1: [ 1] 62 tab 0, row 2, @0x1f80 tl: 8 fb: --H-FL-- lb: 0x0 cc: 2 col 0: [ 2] c1 04 col 1: [ 1] 63 end_of_block_dump End dump data blocks tsn: 10 file#: 8 minblk 16 maxblk 16
這個dump文件開頭對數據庫的環境做了一些描述,中間是一些16進制的內容,最后面是事務和行的一些信息,任何一個事務想修改數據塊,都必需要獲取一個Itl:
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0004.005.00000c41 0x00800c4a.0692.13 ---- 1 fsc 0x0001.00000000 0x02 0x0003.01b.00000cfe 0x008006d1.072a.04 C--- 0 scn 0x0000.01151109
看上面的事務的信息,查看Flag,4個'-'代表有一個事務正在修改數據塊,Lck代表當前鎖定了一條數據,Itl=0x01,其實對應的就是下面的:
tab 0, row 0, @0x1f5c tl: 8 fb: --H-FL-- lb: 0x1 cc: 2 col 0: [ 2] c1 02 col 1: [ 1] 61
當為0x1狀態時,表明該條數據已經被鎖定,加了TX鎖,其他事務想訪問它的時候會被阻塞..
(7)從這個時候Oracle數據塊的強大開始體現出來,如果是其他數據塊(如sqlserver)的話,那么就會等待,而Oracle的一致性讀很牛逼的解決了這個問題,它不讓阻塞,而是讓其他session去undo段里讀,具體的undo地址就是Uba(undo block address)所指示的地址:0x00800c4a.0692.13
我們對這個地址進行轉換,查詢它具體是哪個文件的哪個塊,首先將16進制轉換為10進制,再用相應的工具包進行轉換查詢:
SQL> select to_number('00800c4a','xxxxxxxx') from dual; TO_NUMBER('0080B673','XXXXXXXX') -------------------------------- 8391754 SQL> select dbms_utility.data_block_address_file(8391754) as file#, 2 dbms_utility.data_block_address_block(8391754) as block# 3 from dual; FILE# BLOCK# ---------- ---------- 2 3146
這下很清晰的查看到了,是在第2個數據文件的,第3146塊上,再查詢下第2個數據文件是啥文件
SQL> select tablespace_name,file_id from dba_data_files where file_id=2; TABLESPACE_NAME FILE_ID ------------------------------ ---------- UNDOTBS1 2
哈,這下更清晰了,那個地址指向的就是undo表空間里面的數據塊!
(8)再根據數據文件號和數據塊進行dump:
SQL> select tablespace_name,file_id from dba_data_files where file_id=2; TABLESPACE_NAME FILE_ID ------------------------------ ---------- UNDOTBS1 2 SQL> alter system dump datafile 2 block 3146; System altered. SQL> SELECT d.VALUE || '/' || LOWER(RTRIM(i.INSTANCE, CHR(0))) || '_ora_' || 2 p.spid || '.trc' trace_file_name 3 FROM (SELECT p.spid 4 FROM v$mystat m, v$session s, v$process p 5 WHERE m.statistic# = 1 6 AND s.SID = m.SID 7 AND p.addr = s.paddr) p, 8 (SELECT t.INSTANCE 9 FROM v$thread t, v$parameter v 10 WHERE v.NAME = 'thread' 11 AND (v.VALUE = 0 OR t.thread# = TO_NUMBER(v.VALUE))) i, 12 (SELECT VALUE FROM v$parameter WHERE NAME = 'user_dump_dest') d; TRACE_FILE_NAME -------------------------------------------------------------------------------- /u01/app/oracle/admin/pmisdb/udump/pmisdb_ora_4113.trc
(9)查看dump undo出來的文件:
Dump file /u01/app/oracle/admin/pmisdb/udump/pmisdb_ora_4113.trc Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production With the Partitioning, OLAP, Data Mining and Real Application Testing options ORACLE_HOME = /u01/app/oracle/product/10.2.0/db_1 System name: Linux Node name: pmiscs Release: 2.6.18-92.el5 Version: #1 SMP Tue Apr 29 13:16:12 EDT 2008 Machine: i686 Instance name: pmisdb Redo thread mounted by this instance: 1 Oracle process number: 24 Unix process pid: 4113, image: oracle@pmiscs (TNS V1-V3) *** ACTION NAME:() 2012-09-25 13:31:36.874 *** MODULE NAME:(sqlplus@pmiscs (TNS V1-V3)) 2012-09-25 13:31:36.874 *** SERVICE NAME:(SYS$USERS) 2012-09-25 13:31:36.874 *** SESSION ID:(523.77) 2012-09-25 13:31:36.874 Start dump data blocks tsn: 1 file#: 2 minblk 3146 maxblk 3146 buffer tsn: 1 rdba: 0x00800c4a (2/3146) scn: 0x0000.0117548c seq: 0x01 flg: 0x04 tail: 0x548c0201 frmt: 0x02 chkval: 0x3970 type: 0x02=KTU UNDO BLOCK Hex dump of block: st=0, typ_found=1 Dump of memory from 0x0E101400 to 0x0E103400 ... ******************************************************************************** UNDO BLK: xid: 0x0004.005.00000c41 seq: 0x692 cnt: 0x13 irb: 0x13 icl: 0x0 flg: 0x0000 Rec Offset Rec Offset Rec Offset Rec Offset Rec Offset --------------------------------------------------------------------------- 0x01 0x1f94 0x02 0x1eac 0x03 0x1e04 0x04 0x1d68 0x05 0x1d04 0x06 0x1c68 0x07 0x1c04 0x08 0x1ba8 0x09 0x1b54 0x0a 0x1af8 0x0b 0x1aa4 0x0c 0x1a48 0x0d 0x19f4 0x0e 0x1900 0x0f 0x18b4 0x10 0x17f0 0x11 0x178c 0x12 0x1738 0x13 0x1684 *----------------------------- * Rec #0x1 slt: 0x01 objn: 519(0x00000207) objd: 519 tblspc: 0(0x00000000) * Layer: 10 (Index) opc: 22 rci 0x00 Undo type: Regular undo Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00800c49 *----------------------------- index undo for leaf key operations KTB Redo op: 0x02 ver: 0x01 op: C uba: 0x00800c49.0692.2f Dump kdilk : itl=2, kdxlkflg=0x1 sdc=0 indexid=0x401029 block=0x0040f182 (kdxlpu): purge leaf row key :(10): 06 c5 2b 5f 60 0d 0e 02 c1 1d ... *----------------------------- * Rec #0x12 slt: 0x28 objn: 5141(0x00001415) objd: 5141 tblspc: 0(0x00000000) * Layer: 10 (Index) opc: 22 rci 0x11 Undo type: Regular undo Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00000000 *----------------------------- index undo for leaf key operations KTB Redo op: 0x02 ver: 0x01 op: C uba: 0x00800c4a.0692.11 Dump kdilk : itl=3, kdxlkflg=0x1 sdc=0 indexid=0x402b51 block=0x00402b52 (kdxlpu): purge leaf row key :(10): 02 c1 04 06 00 40 2b 2a 00 08 *----------------------------- * Rec #0x13 slt: 0x05 objn: 69998(0x0001116e) objd: 69998 tblspc: 10(0x0000000a) * Layer: 11 (Row) opc: 1 rci 0x00 Undo type: Regular undo Begin trans Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00000000 *----------------------------- uba: 0x00800c4a.0692.10 ctl max scn: 0x0000.0116f8c0 prv tx scn: 0x0000.0116f8d8 txn start scn: scn: 0x0000.0117548c logon user: 0 prev brb: 8391750 prev bcl: 0 KDO undo record: KTB Redo op: 0x04 ver: 0x01 op: L itl: xid: 0x0009.00f.00000a96 uba: 0x0080019d.0670.22 flg: C--- lkc: 0 scn: 0x0000.0114e6a3 KDO Op code: URP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x02000010 hdba: 0x0200000b itli: 1 ispac: 0 maxfr: 4858 tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12 ncol: 2 nnew: 1 size: 2 col 1: [ 3] 61 2b 2b End dump data blocks tsn: 1 file#: 2 minblk 3146 maxblk 3146
(10)怎么去讀這個dump文件,查找那條有事務的記錄呢?其實在上面undo地址Uba(undo block address)所指示的地址:0x00800c4a.0692.13,已經告訴我們了,0x00800c4a是16進制的地址,而13就是那條update的記錄!我們單獨把那條記錄拿出來:
*----------------------------- * Rec #0x13 slt: 0x05 objn: 69998(0x0001116e) objd: 69998 tblspc: 10(0x0000000a) * Layer: 11 (Row) opc: 1 rci 0x00 Undo type: Regular undo Begin trans Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00000000 *----------------------------- uba: 0x00800c4a.0692.10 ctl max scn: 0x0000.0116f8c0 prv tx scn: 0x0000.0116f8d8 txn start scn: scn: 0x0000.0117548c logon user: 0 prev brb: 8391750 prev bcl: 0 KDO undo record: KTB Redo op: 0x04 ver: 0x01 op: L itl: xid: 0x0009.00f.00000a96 uba: 0x0080019d.0670.22 flg: C--- lkc: 0 scn: 0x0000.0114e6a3 KDO Op code: URP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x02000010 hdba: 0x0200000b itli: 1 ispac: 0 maxfr: 4858 tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12 ncol: 2 nnew: 1 size: 2 col 1: [ 3] 61 2b 2b
(11)對比session A dump出來的信息和從undo dump出來的信息:
--session A
col 1: [ 1] 61
--undo col 1: [ 3] 61 2b 2b
通過對比,可以發現session A的col1的值為61,undo里面的col1的值為61 2b 2b,把這2個值轉換成ascii碼:
SQL> select chr(to_number('61','xx')),chr(to_number('2b','xx')) from dual; CH CH -- -- a +
哈哈,這下清楚了.原來61代表的是'a',2b代表的'+'
(12)這樣,我們就把undo是怎么工作的實驗做完了.把原理再完整的描述一遍:session A對某條記錄做了dml操作,這個操作是在內存中完成的,這個時候在undo里面記錄一條信息,如果滿足了DBWn的條件那么就會寫入到磁盤中,不滿足的話就在內存中,在沒有提交之前,undo的信息一直不會被清除.session B在查詢該條記錄時,因為A沒有提交,所以在itl事務槽中對該條信息有一個記錄,會告訴session B去undo相應的地址查找該條記錄的內容,而不去使用內存中被改變的信息.這就是Oracle的一致性讀.
7.回過頭來,我們再看看相應的數據字典和動態性能視圖:
(1).在之前查找undo的文件和塊的地方,其實oracle已經給了我們一個視圖,告訴了我們相關的信息了
SQL> select t.UBAFIL,t.UBABLK from v$transaction t; UBAFIL UBABLK ---------- ---------- 2 3146
(2).其他幾個動態性能視圖
SQL> select a.USN,a.XACTS from v$rollstat a where xacts <> 0; USN XACTS ---------- ---------- 4 1 SQL> select segment_name from dba_rollback_segs where segment_id = 4; SEGMENT_NAME ------------------------------ _SYSSMU4$ SQL> select * from dba_extents where segment_name = '_SYSSMU4$'; 1 SYS _SYSSMU4$ TYPE2 UNDO UNDOTBS1 0 2 57 65536 8 2 2 SYS _SYSSMU4$ TYPE2 UNDO UNDOTBS1 1 2 225 65536 8 2 3 SYS _SYSSMU4$ TYPE2 UNDO UNDOTBS1 2 2 3081 1048576 128 2 4 SYS _SYSSMU4$ TYPE2 UNDO UNDOTBS1 3 2 2057 1048576 128 2
從v$rollstat的xacts不為0d記錄中可以得到當前能有事務的回滾段,根據回滾段號去dba_rollback_segs查找相應的名字,再根據名字去dba_extents去查找相應的信息,這樣一來,就把所有的知識都聯系起來了.