程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle教程 >> Oracle基本數據改變原理淺析(redo與undo)--oracle核心技術讀書筆記一

Oracle基本數據改變原理淺析(redo與undo)--oracle核心技術讀書筆記一

編輯:Oracle教程

Oracle基本數據改變原理淺析(redo與undo)--oracle核心技術讀書筆記一


在oracle中我們做一些更新操作,oracle底層是怎麼流轉的呢,就是這篇文章要講解的。

一. 原理

假設我們在一個已經更新了很多條分散記錄的OLTP系統中,更新了一行數據。那麼這個更新的真實步驟如下:

1. 創建一個重做改變向量,描述如何往undo塊插入一條undo記錄(也就是描述undo塊的改變)

2. 創建一個重做改變向量,描述數據塊的改變(也就是數據的改變)

3. 合並這兩個重做改變向量為一條日志記錄,並寫到重做日志緩沖區(便於今後重做)

4. 向undo塊插入undo記錄(便於今後回退)

5. 改變數據塊中的數據(這裡才真正改變數據)

下面我們通過一個例子來展示這個過程。

二. 實踐

我們先創建一個表,然後更新表中數據第一個塊的第3,4,5,條記錄,並且在每更新一條後會穿插更新第二個塊的一條記錄。也就是這個更新操作會更新6條記錄,他會改變每一個記錄的第三列------ 一個varchar2類型的字段,將其由xxxxxx(小寫6個字符)改變為YYYYYYYYYY(大寫10個字符)。

1. cmd命令行 以sys 用戶登錄

2. 准備工作(創建幾個存儲過程,用來轉儲塊,轉儲重做日志等)

這些腳本見:http://download.csdn.net/detail/liwenshui322/7912909

3. 准備工作(主要清除回收站刪除信息,設置塊讀取代價,估值計算依據等)

start setenv
set timing off

execute dbms_random.seed(0)

drop table t1;

begin
	begin		execute immediate 'purge recyclebin'; --清空回收站
	exception	when others then null;
	end;

	begin
		dbms_stats.set_system_stats('MBRC',8); --多塊讀取為8塊
		dbms_stats.set_system_stats('MREADTIM',26); --對塊讀取平均時間為26毫秒
		dbms_stats.set_system_stats('SREADTIM',12); --單塊讀取平均時間為30毫秒
		dbms_stats.set_system_stats('CPUSPEED',800); --cpu每秒可執行800,000,000個操作
	exception
		when others then null;
	end;

	begin		execute immediate 'begin dbms_stats.delete_system_stats; end;'; --刪除系統統計信息
	exception	when others then null;
	end;

	begin		execute immediate 'alter session set "_optimizer_cost_model"=io'; --基於io來計算估值
	exception	when others then null;
	end;
end;
/

4. 創建表與索引

create table t1
as
select
	2 * rownum - 1			id,
	rownum				n1,
	cast('xxxxxx' as varchar2(10))	v1,
	rpad('0',100,'0')		padding
from
	all_objects
where
	rownum <= 60
union all
select
	2 * rownum			id,
	rownum				n1,
	cast('xxxxxx' as varchar2(10))	v1,
	rpad('0',100,'0')		padding
from
	all_objects
where
	rownum <= 60
;

create index t1_i1 on t1(id);

5. 統計表信息

begin
	dbms_stats.gather_table_stats(
		ownname		 => user,
		tabname		 =>'T1',
		method_opt 	 => 'for all columns size 1'
	);
end;
/

6.查看表占用的塊情況,和每一個塊有多少條數據

select 
	dbms_rowid.rowid_block_number(rowid)	block_number, 
	count(*)				rows_per_block
from 
	t1 
group by 
	dbms_rowid.rowid_block_number(rowid)
order by
	block_number
;
\

我們會看到,總共占用兩個塊,每一個塊都有60條記錄

7. 轉儲數據塊

alter system switch logfile;

execute dump_seg('t1')

8. 做更新

update
	/*+ index(t1 t1_i1) */
	t1
set
	v1 = 'YYYYYYYYYY'
where
	id between 5 and 9
;
9. 轉儲更新塊之後的數據塊和undo塊(發生檢查點語句執行後,下一個語句等5,6s再執行,發生檢查點只是告訴oracle將髒數據寫入磁盤,需要一點時間)
pause Query the IMU structures now  (@core_imu_01.sql)
alter system checkpoint;--發生檢查點,讓數據寫到磁盤
execute dump_seg('t1')
execute dump_undo_block
10. 轉儲redo塊
rollback;
commit;

execute dump_log
11. 定位轉儲信息文件位置
select sid from v$mystat where rownum=1;--查詢結果傳入下一個sql
SELECT d.value||'/'||lower(rtrim(i.instance, chr(0)))||'_ora_'||p.spid||'.trc' trace_file_name 
from 
   ( select p.spid from v$session s, v$process p 
     where s.sid='133' and p.addr = s.paddr) p, 
   ( select t.instance from v$thread t,v$parameter v 
     where v.name = 'thread' and (v.value = 0 or t.thread# = to_number(v.value))) i,
   ( select value from v$parameter where name = 'user_dump_dest') d;
\

12. 打開文件
下面看幾個關鍵部分,我們看第一個塊的第5條數據,我們將這一行數據的第三列由xxxxxx改成了YYYYYYYYYY。

update之前:

tab 0, row 4, @0x1d3f
tl: 117 fb: --H-FL-- lb: 0x0  cc: 4
col  0: [ 2]  c1 0a
col  1: [ 2]  c1 06
col  2: [ 6]  78 78 78 78 78 78
col  3: [100]
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
我們看到col2長度是6,然後是6個78(x的十六進制ASCII碼是78)。

update之後:

tab 0, row 4, @0x2a7
tl: 121 fb: --H-FL-- lb: 0x2  cc: 4
col  0: [ 2]  c1 0a
col  1: [ 2]  c1 06
col  2: [10]  59 59 59 59 59 59 59 59 59 59
col  3: [100]
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
我們可以看到col2長度變成了10,是10個59(Y的十六進制ASCII碼是59),同時我們看到行地址由@0x1d3f變成了@0x2a7,說明這一行的空間容不下新增的數據,換了一個地方。(檢查行目錄也能發現這一點)同時,我們能看到lb(lock byte)由0x0變成了0x2,表明這條記錄被該塊事務槽列表中的第二個事務槽所標識的事務鎖定。事務槽可以在塊首部看到。

下面,看第5條數據在redo裡面保存的是什麼(怎麼保證數據的重做),在文件裡面搜索 tabn: 0 slot: 4(0x4) flag: 0x2c lock: 2, 我們會找到這麼一段描述

CHANGE #6 TYP:0 CLS:1 AFN:1 DBA:0x004161c9 OBJ:77125 SCN:0x0000.002796b6 SEQ:2 OP:11.5 ENC:0 RBL:0
KTB Redo 
op: 0x02  ver: 0x01  
compat bit: 4 (post-11) padding: 0
op: C  uba: 0x00c0055a.0123.27
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x004161c9  hdba: 0x004161c8
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 4(0x4) flag: 0x2c lock: 2 ckix: 50
ncol: 4 nnew: 1 size: 4
col  2: [10]  59 59 59 59 59 59 59 59 59 59
這描述的是一個改變世界,我們看第6行 op code:是URP(更新行片),第七行我們可以看到更新的塊地址bdba和所在段的地址hdba。

第八行itli: 2 表明執行更新操作的事務正在使用第二個事務槽,跟數據塊裡面看到的一致。

第九行tabn: 0 slot: 4 表明我們在更新第一張表(一個塊可能存儲多個表的數據)的第5條記錄。

最後兩行,我們可以看出這條記錄有4列(nclo:4),修改了一列(nnew:1),長度增加了4(size:4).並將第3列的值改成了YYYYYYYYYY。(保存了修改後的數據,方便重做)

接下來,看第5條數據在undo裡面怎麼保存的(怎麼保證數據的回退),在文件裡面搜索tabn: 0 slot: 4(0x4) flag: 0x2c,我們會找到如下一段描述:

*-----------------------------
* Rec #0x27  slt: 0x04  objn: 77125(0x00012d45)  objd: 77125  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x26   
Undo type:  Regular undo   Last buffer split:  No 
Temp Object:  No 
Tablespace Undo:  No 
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo 
op: 0x02  ver: 0x01  
compat bit: 4 (post-11) padding: 0
op: C  uba: 0x00c0055a.0123.25
KDO Op code: URP Disabled row dependencies
  xtype: XA flags: 0x00000000  bdba: 0x004161c9  hdba: 0x004161c8
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0 ckix: 50
ncol: 4 nnew: 1 size: -4
col  2: [ 6]  78 78 78 78 78 78
主要關注下面的六行數據,其實跟前面redo裡面的數據差不多,就是size=-4,col2變成了6個78(x的十六進制ASCII碼是78)。(保證數據能夠回去以前的版本)

最後,我們可以在轉儲的redo裡面尋找undo塊改變的描述,文件裡面搜索tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0,我們會找到如下一段描述:

CHANGE #11 TYP:0 CLS:36 AFN:3 DBA:0x00c0055a OBJ:4294967295 SCN:0x0000.002796b6 SEQ:4 OP:5.1 ENC:0 RBL:0
ktudb redo: siz: 92 spc: 4078 flg: 0x0022 seq: 0x0123 rec: 0x27
            xid:  0x000a.004.00000467  
ktubu redo: slt: 4 rci: 38 opc: 11.1 objn: 77125 objd: 77125 tsn: 0
Undo type:  Regular undo       Undo type:  Last buffer split:  No 
Tablespace Undo:  No 
             0x00000000
KDO undo record:
KTB Redo 
op: 0x02  ver: 0x01  
compat bit: 4 (post-11) padding: 0
op: C  uba: 0x00c0055a.0123.25
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x004161c9  hdba: 0x004161c8
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0 ckix: 50
ncol: 4 nnew: 1 size: -4
col  2: [ 6]  78 78 78 78 78 78
第五行,代表這是一個undo塊改變的描述,我們可以看到倒數幾行跟undo裡面的數據非常相似,因為這裡記錄的就是undo塊的改變。

自此,我們基本上可以看清楚oracle是怎麼描述數據的改變,然後才去真正去改變數據的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved