块和表不是一一对应的,一个表很大肯定对应多个块,但如果表很小可以多表放一个块。Oracle读数据去内存是读一个块不是一个记录。
《 expert oracle database architecture 9i and 10g programming techniques and solutions》中所述
The table directory, if present, contains information about the tables that store rows in this block(data from more than one table may be stored on the same block).
下面描述下事务的修改过程
1、第一步
一个事务开始,首先在undo表空间里面找相对空闲的undo段的段头块的事务表里的一行,将自己的xid写上,同时在undo段里面分配一个空块作为回滚块,同时将回滚块的地址写到事务表的事务槽里。
2、第二步
要修改具体的数据了
修改任何一个数据库都是要在数据块块的头部获取一个事务槽,然后写上xid,获取到事务槽就开始修改数据块,修改数据块的时候,还将修改前的数据写到回滚块里,因为这个事务已经分配一个回滚块了,就直接写到回滚块里面去,同时将这个数据块所对应的回滚块的地址也写到数据块的事务槽里,回滚数据写完了,接下来修改实实在在的行,第一个数据块(应该是行吧)修改完了,再修改第二个数据块(行?),同样的操作继续修改数据块(行?),同时如果这个时候一个回滚块不够,要分配第二第三个回滚块(一个数据块对应多个rollback块,不是改一次对应一次块,而是改一次在rollback中加行),同是一个事务的回滚块之间要串起来,由新到旧的指向,同时如果加了回滚块,会把最新的回滚块信息uba地址写到该事务的事务表里(之前的被覆盖了,串联起来找?)。
以上是整个事务的操作流程,这三个回滚块串起来以及事务表的uba指向最新的回滚块,这样为了更方便的回滚。在每个数据块里面,直接写uba指向自己的undo数据块,为了更方便的构造cr块及一致性读。同时,一个事务信息在两个位置都有,数据块的事务槽(ITL)里有,undo段头块(undo segment header)的事务表里有,数据块的事务槽指向事务表,这个和Oracle的一种提交方式有关!
一个事务开始以后,Oracle修改了四个地方:(上图 每次还要写REDO呢)
回滚段的段头块的事务表被修改;
回滚块被修改;
数据块的事务槽被修改;
数据块的数据行被修改。
这四个地方的数据都在数据块里面,数据块的任务地方的改变都会产生redo,redo不仅仅记录的是数据行的改变,事务槽的改变、undo块的改变、事务表的改变一样都会产生redo,这就是一个事务的操作流程。
本帖最后由 mlx_861201 于 2013-3-23 10:45 编辑
Commit过程中redo、undo、数据块交互过程实验
实验目的
Commit过程中redo、undo、数据块交互过程 。如下图:
undo数据块、undo段头块、数据块之间的关系
1、 创建cricket用户。
create user cricket identified by cricket;
grant dba to cricket;
2、 创建score表。
create table score(
TEAM varchar2(30),
runs number,
WICKETS number
)
3、 构成数据
Insert into score values(‘ENG’,841,3);
Insert into score values(‘AUS’,77,8);
Commit;
实验过程1、执行第一句UPDATE
Update score set runs=77 where team =’AUS’;
A、undo段头块变化情况。
通过select * from v$transaction;查询活动的事务,获得事务所在undo段,以及undo块地址。本例事务所在undo段为10号段,事务槽为15号事务槽,事务槽seq为4690,undo块为3 号文件第 2950块。
Dump出undo段段头块。altersystem dump undo header ' _SYSSMU10_4131489474$';
可以看到undo段头块事务表的第15号事务槽的state 是10,cflags是0x80,wrap是0x1213,dba为undo块地址 ,转换为文件号和块号为3+2950,内容与v$transaction反映的一致。
B、undo块变化情况。
通过v$transaction可以看到该事务对应的undo块为3号文件的2950块。
Dump出该undo块: alter system dump datafile 3 block 2950。
查看undo块概况信息:
Xid:事务号,xid由 undosegment no + slot no + seq no组成。
Seq:该undo块的序列号,表示该undo块被覆盖的次数。该值就是v$transaction视图中的ubaseq。
Cnt:该undo块中undo记录数。该值就是v$transaction试图中的UBAREC。
irb:表示当前事务的前进项存放的记录号,该例中可以直接到底0X2e记录上查找。 Rec:undo记录号。
Offset:偏移量。第一个offset的偏移量是4080 。
查看第0X2e个undo 记录信息:
Rec #0x2e:该undo记录的号码。
Slt:事务表槽号。
Objn:对应数据块对象号。
Tblspc:对应数据块表空间号。
Col:保存前进项值。Ox4c-1=77。
Bdab:数据块地址。
Hdba:数据块所在段的段头块。
C、数据块头变化
通过 rowid 可以知道数据块存放在4号文件550号块。
Dump该数据块,查看块头事务槽。altersystem dump datafile 4 block 550。
1号事务槽的Xid与undo块保存的Xid一致。Uba指向undo块,028b表示块的覆盖次数,2e表示undo记录号。Flag为----表示事务未提交。Lck为1表示有一行被锁定。
D、数据行变化
查看数据块中的行信息
lb:0x1表示该行数据被1号事务槽锁定。第二列数据已经被修改Ox4c-1=75。
E、redo变化
1、查找正在使用的logfile
SELECT * FROM v$LOGFILEWHERE GROUP# =(Select GROUP# from v$log where status = ‘CURRENT’);
2、dump正在使用的日志文件(
/u01/app/oracle/oradata/orcl/redo04.log);
alter
system
dump
logfile
'redoFile'
;
找到与当前修改对象相关的内容(对象号76014)。
包含三个改变向量(CHANGE),
Change1是对数据块修改的redo信息。
DBA:数据块地址。
OBJ:数据块所在对象号。
OP:11.19。表示update row array
Col 1:[ 2] c1 4c 保存修改后的数据。
Chage2是对undo段头块修改参数的redo信息。
DBA:undo段头块地址。
OBJ:修改块所在对象号。
OP:5.2表示对undo段头块的修改。
UBA:undo块地址。
Change3是对undo数据块的修改。
DBA:undo块地址。
XID:事务号。
BDBA:数据块地址。
HDBA:数据块段头块地址。
UBA:undo块地址。
2、执行commit;A、undo段头块变化情况。
Dump出undo段段头块。alter system dump undo header ' _SYSSMU4_1003442803$';
Commit后段头事务表如下:
Commit前段头事务表如下:
变化:事务槽15的state变成了9,cflags变为00,uel变成了无限大(ffff),scn变成了提交是的scn。
B、undo块变化情况。
Dump出undo块。Dump出该undo块: alter systemdump datafile 3 block 2950。
Undo块头信息没有发生变化,直接查看0x2e号undo条目,可以发现没有任何变化。
通过 rowid 可以知道数据块存放在4号文件550号块。
先刷新内存alter systemflush buffer_cache。
Dump该数据块,查看块头事务槽。altersystem dump datafile 4 block 550。
可以看到没有变化,commit之后flag还是“--U-”, lck还是“1”,fscchans。这就是快速提交。
D、数据行变化情况。
查看数据块中的行信息:
lb:0x1表示该行数据被1号事务槽锁定。第二列数据已经被修改Ox4c-1=75。
E、redo变化情况。
Dump出当前redo日志文件:
alter
system
dump
logfile
'redoFile'
;
可以看到多了两条重做记录,
1、数据提交的重做记录,里面只有一个change:
增加的这条change为对undo段头事务表的修改产生的redo信息。OP:5.4
2、一个为写文件的重做记录
产生了一天写入redo条目。OP:23.1
|
1.jpg (161.05 KB, 下载次数: 9)
2.jpg (61.07 KB, 下载次数: 8)
3.jpg (69.15 KB, 下载次数: 14)
6.jpg (34.07 KB, 下载次数: 15)
SYS用户可查询 buffer header数据,可以从数据库的数据字典表中查询得到,这张字典表就是x$bh,其中的bh就是指buffer headers,每个buffer在x$bh中都存在一条记录. buffer header中存储每个buffer容纳的数据块的文件号,块地址,状态等重要信息,根据这些信息,结合dba_extents视图,可以很容易地找到每个buffer对应的对象信息: x$bh中还有一个重要的字段TCH,TCH为Touch的缩写,表示一个Buffer的访问次数,buffer被访问的次数越多,说明该buffer就越抢手,也就可能存在热块竞争的问题 可以结合DUMP BH来对比X$BH中字段。
SQL> desc v$bh;
Name Type
-----------------------------
ADDR RAW(4) --Hex address of the Buffer Header.视图中某行的地址-缓冲区头的十六进制地址
INDX NUMBER --Buffer Header number
INST_ID NUMBER
HLADDR RAW(4) --即hash chain latch address可以和v$latch_children.addr关联,把latch和数据块关联起来
BLSIZ NUMBER --块大小
NXT_HASH RAW(4) --下一个BH的HASH值
PRV_HASH RAW(4) --前一个BH的HASH值
NXT_REPL RAW(4) --下一个BH的在LRU链上HASH值
PRV_REPL RAW(4) --前一个BH的在LRU链上HASH值
FLAG NUMBER ---块的状态,在BH中可能是buffer_dirty block_written_once redo_since_read,详见最后
FLAG2 NUMBER
LOBID NUMBER
RFLAG NUMBER
SFLAG NUMBER
LRU_FLAG NUMBER --- LRU_FLAG,详见最后
TS# NUMBER --tablespace number表空间号
FILE# NUMBER --块在数据库内绝对文件号
DBARFIL NUMBER --块的相对文件号
DBABLK NUMBER --块号--在数据文件上的
CLASS NUMBER --太长,见最下面。
STATE NUMBER --太长,见最下面。
MODE_HELD NUMBER --
CHANGES NUMBER
CSTATE NUMBER
LE_ADDR RAW(4) --Lock Element address (OPS)
DIRTY_QUEUE NUMBER --LRUW上的buffer
SET_DS RAW(4) -- Buffer cache set this buffer is under
OBJ NUMBER --对象号
BA RAW(4) --BUFFER ADDRESS--在内存中的地址
CR_SCN_BAS NUMBER --Consistent Read SCN base 一致读SCN低位
CR_SCN_WRP NUMBER --Consistent Read SCN wrap 一致读SCN高位
CR_XID_USN NUMBER -- CR XID Undo segment no
CR_XID_SLT NUMBER -- CR XID slot
CR_XID_SQN NUMBER --CR XID Sequence
CR_UBA_FIL NUMBER -- CR UBA file
CR_UBA_BLK NUMBER -- CR UBA Block
CR_UBA_SEQ NUMBER --CR UBA sequence
CR_UBA_REC NUMBER --CR UBA record
CR_SFL NUMBER --
CR_CLS_BAS NUMBER
CR_CLS_WRP NUMBER
LRBA_SEQ NUMBER --Lowest RBA needed to recover block in cache
LRBA_BNO NUMBER
HSCN_BAS NUMBER ---SCN的低位
HSCN_WRP NUMBER ---SCN的高位
HSUB_SCN NUMBER
US_NXT RAW(4)
US_PRV RAW(4)
WA_NXT RAW(4)
WA_PRV RAW(4)
OQ_NXT RAW(4) --- 对象队列前一个HASH值
OQ_PRV RAW(4) --- 对象队列后一个HASH值
AQ_NXT RAW(4) ---辅助对象队列前一个HASH值
AQ_PRV RAW(4) ---辅助对象队列后一个HASH值
OBJ_FLAG NUMBER
TCH NUMBER --Touch的缩写,表示一个Buffer的访问次数
TIM NUMBER --Touch Time
CR_RFCNT NUMBER
SHR_RFCNT NUMBER
bh即描述了数据块块头的信息,buffer header描述了数据块的信息.
chain都是这样的。
下面查询可获得数据库最繁忙的Buffer
select * from (select addr,ts#,file#,dbarfil,dbablk,tch from x$bh order by tch desc) where rownum<11;
在结合dba_extents中的信息,可以查询得到这些热点Buffer都来自哪些对象:
select e.owner,e.segment_name,e.segment_type from dba_extents e,(select * from(select addr,ts#,file#,dbarfil,dbablk,tch from x$bh order by tch desc)
where rownum<11)b
where e.relative_fno=b.dbarfil
and e.block_id<=b.dbablk
and e.block_id+e.blocks>b.dbablk;
热点块与竞争的解决可以从v$latch_children中查询具体的子Latch信息
select * from(select addr,child#,gets,misses,sleeps,immediate_gets igets,immediate_misses imiss,spin_gets sgets
from v$latch_children
where name='cache buffers chains'
order by sleeps desc)
where rownum<11;
X$BH中还存在另外一个关键字段HLADDR,即Hash Chain Latch Address,这个字段可以和v$latch_child.addr进行连接,这样就可以把具体的Latch竞争和数据块关联起来,在结合dba_extents视图,就可以找到具体的热点竞争对象,找到具体热点竞争对象之后,可以结合v$sqlarea或者v$sqltext,找到频繁操作这些对象的SQL,然后对其进行优化,即可缓解或解决热点块竞争的问题。
可以通过如下查询获取当前持有最热点数据块的Latch及Buffer信息:
select b.addr,a.ts#,a.dbarfil,a.tch,b.gets,b.misses,b.sleeps from
(select * from (
select addr,ts#,file#,dbarfil,dbablk,tch,hladdr from x$bh order by
tch desc) where rownum<11) a,
(select addr,gets,misses,sleeps from v$latch_children where
name='cache buffers chains')b
where a.hladdr=b.addr
根据如下的sql找到这些热点buffer的对象的信息
select e.owner,e.segment_name,e.segment_type from dba_extents e,
(select * from (select addr,ts#,file#,dbarfil,dbablk,tch from x$bh
order by tch desc)
where rownum<11) b
where e.relative_fno=b.dbarfil and e.block_id<=b.dbablk
and e.block_id+e.blocks>b.dbablk;
结合v$sqltext或v$sqlarea,可以找到操作这些对象的相关SQL,继续查询:
找到sql之后,就可以通过优化SQL减少数据的访问。
break on hash_value skip 1;
select hash_value,sql_text from v$sqltext
where (hash_value,address) in (
select a.hash_value,a.address from v$sqltext a,
(select distinct a.owner,a.segment_name,a.segment_type
from dba_extents a,
(select dbarfil,dbablk from (
select dbarfil,dbablk from x$bh order by tch desc)
where rownum<11) b
where a.relative_fno=b.dbarfil
and a.block_id<=b.dbablk
and a.block_id+a.blocks>b.dbablk) b
where a.sql_text like '%'||b.segment_name||'%' and b.segment_type='TABLE'
)order by hash_value,address,piece;