在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_log11. 定位转储信息文件位置
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