完成本课程的学习后,您应该能够:
•数据库总体的逻辑结构
数据库逻辑结构为数据块(Data Block)、数据扩展(Extent)、和段(Segment); 物理结构为数据文件。
Block是最精细的数据存储粒度,一个数据块相当于磁盘上一段连续的物理存储空间,oracle每次访问数据的单位是block。
Extent是为存储数据而分配的一组连续的block,只能在一个数据文件中。
Segment则是由一个或多个Extent。一张表可以看做是一个段,一个索引可以做作是一个段。查看段的分类select distinct segment_type from user_segments 。可以跨数据文件。
数据逻辑上存储表空间(Tablespace)中,而物理上则存储于属于表空间的数据文件(data file)中。
1.1表空间管理
依赖数据字典表来跟踪空间使用(已经过时)
使用位图(而不是数据字典表)来跟踪使用和空闲空间在表空间本地管理的情况下,Oracle先在表空间第一候选的数据文件上寻找可以分配给第一个区段的空间,然后查看数据文件的位图来查看是否有足够数目的空闲块。如果数据文件没有足够的空闲块,然后Oracle会转向新的数据文件。
1.2段空间分配方式
Uniform
AUTOALLOCATE
select * from dba_segments where owner='SCOTT';
1.3表中的段分配
一个表在创建之后,除非进行truncate,否则数据库段中分配的所有数据块都只属于这个表。Oracle在一个块有足够空间的情况下插入新行。甚至你删除了表的所有行,Oracle也不会释放数据块,表空间的其他对象也无法使用这些数据块。
1.4索引的段分配
只要索引存在,索引段中的所有区段都属于索引段。当你删除(drop)索引或者相关的表时,Oracle释放区段,本表空间的其他对象可以使用这部分空间。
1.5临时段的段分配
当Oracle执行完一个需要临时段的语句时,Oracle自动删除临时段。如果排序操作能够在内存中进行,Oracle不会创建这个段。
1.6回滚段的段分配
Oracle周期性的检查数据库的回滚段,来检查是否超过了它们的最优尺寸。如果回滚段远大于最优尺寸(也就是说,有太多的区段),然后Oracle会自动从回滚段中释放一个或者多个区段。
1.7区的大小?
在我们默认的本地管理表空间中,extent是通过bitmap位图块来管理,Oracle通过内部的算法来自动决定下一个extent的大小
create table t(id number);select * from user_extents where segment_name='T'drop table t purge; create table t as select * from dba_objects;select * from user_extents where segment_name='T';
1.8小结:
seg/obj: 0x180a2 csc: 0x95a.e56f14c6 itc: 2 flg: E typ: 1 - DATA brn: 0 bdba: 0x181e541 ver: 0x01 opc: 0 inc: 0 exflg: 0 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0002.002.000072bb 0x0081fa06.1e1c.26 --U- 2 fsc 0x0000.e56f14ce 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 。。。。。。。。。。。省略。。。。。。。。。。。 0x14:pri[1] offs=0x1f88 block_row_dump: tab 0, row 0, @0x1f90 tl: 8 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 4] d6 d0 b9 fa tab 0, row 1, @0x1f88 tl: 8 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 4] c3 c0 b9 fa end_of_block_dump
dump的文件在 盘符:\oracle\product\10.2.0\admin\ordb10\udump
SQL>alter session set tracefile_identifier = 'Look_For_Me'; SQL>create table test(name varchar2(10)); SQL>insert into test values('中国'); SQL>nsert into test values('美国'); SQL>commit; SQL>select rowid, dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id, dbms_rowid.rowid_block_number(rowid) block_id, dbms_rowid.rowid_row_number(rowid) num from test; SQL>alter system dump datafile 6 block 124232;
--在PL/SQL命令窗口要 set serveroutput on --查看表 exec show_space('watch_log_event','auto','table'); exec show_space('watch_log_event','auto','table','Y'); --查看索引 exec show_space('INDEX_TX_LOCK','auto','index'); exec show_space('INDEX_TX_LOCK','auto','index','Y'); create or replace procedure show_space ( p_segname_1 in varchar2, p_space in varchar2 default 'MANUAL', p_type_1 in varchar2 default 'TABLE' , p_analyzed in varchar2 default 'N', p_owner_1 in varchar2 default user) as p_segname varchar2(100); p_type varchar2(10); p_owner varchar2(30); l_unformatted_blocks number; l_unformatted_bytes number; l_fs1_blocks number; l_fs1_bytes number; l_fs2_blocks number; l_fs2_bytes number; l_fs3_blocks number; l_fs3_bytes number; l_fs4_blocks number; l_fs4_bytes number; l_full_blocks number; l_full_bytes number; l_free_blks number; l_total_blocks number; l_total_bytes number; l_unused_blocks number; l_unused_bytes number; l_LastUsedExtFileId number; l_LastUsedExtBlockId number; l_LAST_USED_BLOCK number; procedure p( p_label in varchar2, p_num in number ) is begin dbms_output.put_line( rpad(p_label,40,'.') || p_num ); end; begin p_segname := upper(p_segname_1); p_owner := upper(p_owner_1); p_type := p_type_1; if (p_type_1 = 'INDEX' or p_type_1 = 'index') then p_type := 'INDEX'; end if; if (p_type_1 = 'TABLE' or p_type_1 = 'table') then p_type := 'TABLE'; end if; if (p_type_1 = 'CLUSTER' or p_type_1 = 'cluster') then p_type := 'CLUSTER'; end if; dbms_space.unused_space ( segment_owner => p_owner, segment_name => p_segname, segment_type => p_type, total_blocks => l_total_blocks, total_bytes => l_total_bytes, unused_blocks => l_unused_blocks, unused_bytes => l_unused_bytes, LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId, LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId, LAST_USED_BLOCK => l_LAST_USED_BLOCK ); if p_space = 'MANUAL' or (p_space <> 'auto' and p_space <> 'AUTO') then dbms_space.free_blocks ( segment_owner => p_owner, segment_name => p_segname, segment_type => p_type, freelist_group_id => 0, free_blks => l_free_blks ); p( 'Free Blocks', l_free_blks ); end if; p( 'Total Blocks', l_total_blocks ); p( 'Total Bytes', l_total_bytes ); p( 'Unused Blocks', l_unused_blocks ); p( 'Unused Bytes', l_unused_bytes ); p( 'Last Used Ext FileId', l_LastUsedExtFileId ); p( 'Last Used Ext BlockId', l_LastUsedExtBlockId ); p( 'Last Used Block', l_LAST_USED_BLOCK ); /*IF the segment is analyzed */ if p_analyzed = 'Y' then dbms_space.space_usage(segment_owner => p_owner , segment_name => p_segname , segment_type => p_type , unformatted_blocks => l_unformatted_blocks , unformatted_bytes => l_unformatted_bytes, fs1_blocks => l_fs1_blocks, fs1_bytes => l_fs1_bytes , fs2_blocks => l_fs2_blocks, fs2_bytes => l_fs2_bytes, fs3_blocks => l_fs3_blocks , fs3_bytes => l_fs3_bytes, fs4_blocks => l_fs4_blocks, fs4_bytes => l_fs4_bytes, full_blocks => l_full_blocks, full_bytes => l_full_bytes); dbms_output.put_line(rpad(' ',50,'*')); dbms_output.put_line('The segment is analyzed'); p( '0% -- 25% free space blocks', l_fs1_blocks); p( '0% -- 25% free space bytes', l_fs1_bytes); p( '25% -- 50% free space blocks', l_fs2_blocks); p( '25% -- 50% free space bytes', l_fs2_bytes); p( '50% -- 75% free space blocks', l_fs3_blocks); p( '50% -- 75% free space bytes', l_fs3_bytes); p( '75% -- 100% free space blocks', l_fs4_blocks); p( '75% -- 100% free space bytes', l_fs4_bytes); p( 'Unused Blocks', l_unformatted_blocks ); p( 'Unused Bytes', l_unformatted_bytes ); p( 'Total Blocks', l_full_blocks); p( 'Total bytes', l_full_bytes); end if; end; SQL> exec show_space('TEST','auto','table'); Total Blocks............................8 Total Bytes.............................65536 Unused Blocks...........................0 Unused Bytes............................0 Last Used Ext FileId....................6 Last Used Ext BlockId...................124225 Last Used Block.........................8 官方文档解释: Parameter Description segment_owner Schema name of the segment to be analyzed. segment_name Segment name of the segment to be analyzed. segment_type Type of the segment to be analyzed (TABLE,INDEX, orCLUSTER): •TABLE •TABLE PARTITION •TABLE SUBPARTITION •INDEX •INDEX PARTITION •INDEX SUBPARTITION •CLUSTER •LOB •LOB PARTITION •LOB SUBPARTITION total_blocks Returns total number of blocks in the segment. total_bytes Returns total number of blocks in the segment, in bytes. unused_blocks Returns number of blocks which are not used. unused_bytes Returns, in bytes, number of blocks which are not used. last_used_extent_ file_id Returns the file ID of the last extent which contains data. last_used_extent_ block_id Returns the starting block ID of the last extent which contains data. last_used_block Returns the last block within this extent which contains data. partition_name Partition name of the segment to be analyzed. This is only used for partitioned tables; the name of subpartition should be used when partitioning is compose.
2.1Block内容解析--The Transaction Header
一共占据48bytes,包括24bytes的控制信息,和一系列的Interested Transaction Slot (ITS)。这些ITS组合在一起称为Interested Transaction List (ITL)。初始的ITL slot 数量由 INITRANS 决定(index branch block 只有1个slot)。如果有足够的剩余空间,oracle会根据需要动态的分配这些slot,直到受到空间限制或者达到了MAXTRANS。
1.要研究block中所有的含义,请参考http://blog.csdn.net/guogang83/article/details/8640555
2.二进制、八进制、十进制、十六进制的相互转换请参考http://blog.csdn.net/guogang83/article/details/8002014
select pkg_number_trans.f_hex_to_dec(‘1b5’) from dual;--十六进制转换为十进制
2.2Block内容解析-- Data Area
包括14bytes的data header,4bytes/table的table dictionary,2bytes/row的row dictionary。table dictionary主要用于cluster block中,只不过table block中的table dictionary只有一个table。
•select chr(to_number('b9fa','xxxx')) from dual;--国
1.不同的数据类型解析规则不一样,想要知道更多的数据类型解析,请参考http://blog.csdn.net/guogang83/article/details/8010105
实验2.2 Oracle如何实现行锁
通过数据库的物理结构实现行锁
insert into test values('韩国'); insert into test values('朝鲜'); insert into test values('越南'); commit; alter system dump datafile 6 block 124232; 重新开一个session: select * from test where name='韩国' for update; 重新开一个session: select * from test where name='韩国' for update;
2.3Block参数PCTFREE、PCTUSED
思考题:建表时的参数含义?
create table TEST(ID NUMBER)
tablespace DFWMS
pctfree 10
initrans 1
maxtrans 255
storage
( initial 64
minextents 1
maxextents unlimited
);
3.行迁移与行链接
3.1 行迁移
原本存储在一个数据块内的数据行,因为更新操作导致长度增长,而所在数据块的可用空间也不能容纳增长后的数据行。在这种情况下,oracle将此行数据迁移到新的数据块中,oracle在被迁移数据行原本所在位置保存一个指向新数据块的指针。被迁移数据行的rowid保持不变。当数据行发生链接或迁移时,对其访问将会造成I/O性能降低,因为oracle为获取这些数据行的数据时,必须访问更多的数据块。
实验3.1:实验产生行迁移
要想构造出行迁移,我们要有点特殊的手段,需要尽量让数据块满,只要把pctfree设置小一点就可以了。插入一些数据后,找到一个块满的,然后做修改,由于块中已没有空间支持行链接,则发生行迁移,是整行发生迁移。
SQL>drop table t purge; SQL>select * from t; SQL>create table t (a varchar2(4000), b varchar2(4000), c varchar2(4000)) pctfree 2; SQL>begin for i in 1.. 1000 loop insert into t values(i,i,i); end loop; commit; end; SQL>select count(1),block_id from (select rowid,a, dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id, dbms_rowid.rowid_block_number(rowid) block_id, dbms_rowid.rowid_row_number(rowid) num from t )group by block_id; SQL>select max(a) max_a,min(a) min_a from(select rowid,a, dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id, dbms_rowid.rowid_block_number(rowid) block_id, dbms_rowid.rowid_row_number(rowid) num from t) where block_id=1471; SQL>update t set b=lpad('2', 4000, '2'),c=lpad('2', 4000, '2') where to_number(a) >484 and to_number(a) <950; SQL>alter system dump datafile 4 block 1471; trace的文件:黄色部分就是行迁移 tl: 9 fb: --H----- lb: 0x2 cc: 0nrid: 0x010005f0.0tab 0, row 10, @0x1e89tl: 9 fb: --H----- lb: 0x2 cc: 0nrid: 0x010005f7.0tab 0, row 11, @0x1e80tl: 9 fb: --H----- lb: 0x2 cc: 0nrid: 0x010005f1.0tab 0, row 12, @0x1e77tl: 9 fb: --H----- lb: 0x2 cc: 0nrid: 0x010005f3.0tab 0, row 13, @0x1e43tl: 52 fb: --H-F--N lb: 0x2 cc: 2nrid: 0x010005f4.0col 0: [ 3] 34 39 38col 1: [38] 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 转换nrid: select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('018047bc', 'xxxxxxxxxx')) file#, dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('018047bc', 'xxxxxxxxxx')) block# from dual;
3.2 行链接
当一行数据被插入时一个数据块就无法容纳,在这种情况下oracle将这行数据存储在段内的一个数据块链中。在插入数据量大的行时常会发生行链接(row chaining)。例如一个包含数据类型为long或long raw列的数据行,此时行链接不可避免。
SQL>create table t (a varchar2(4000), b varchar2(4000), c varchar2(4000)); insert into t values (lpad('1', 4000, '1'), lpad('2', 4000, '2'), lpad('3', 4000, '3')); commit; select rowid, dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id, dbms_rowid.rowid_block_number(rowid) block_id, dbms_rowid.rowid_row_number(rowid) num from t; ROWID OBJECT_ID FILE_ID BLOCK_ID NUM------------------ ---------- ---------- ---------- ----------AAAXUTAAGAAAEe9AAA 95507 6 18365 0 alter system dump datafile 6 block 18365; System altered data_block_dump,data header at 0xa768464===============tsiz: 0x1f98hsiz: 0x14pbl: 0x0a768464bdba: 0x018047bd 76543210flag=--------ntab=1nrow=1frre=-1fsbo=0x14fseo=0x40avsp=0xfd8tosp=0xfd80xe:pti[0] nrow=1 offs=00x12:pri[0] offs=0x40block_row_dump:tab 0, row 0, @0x40tl: 4012 fb: --H-F--- lb: 0x1 cc: 1nrid: 0x018047bc.0--链接的块的地址col 0: [4000] 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 ................................省略.....................................end_of_block_dump 转换nrid: select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('018047bc', 'xxxxxxxxxx')) file#, dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('018047bc', 'xxxxxxxxxx')) block# from dual; alter system dump datafile 6 block 18364; System altered 链接块1data_block_dump,data header at 0xa76847c===============tsiz: 0x1f80hsiz: 0x14pbl: 0x0a76847cbdba: 0x018047bc 76543210flag=--------ntab=1nrow=1frre=-1fsbo=0x14fseo=0x2eavsp=0xfc0tosp=0xfc00xe:pti[0] nrow=1 offs=00x12:pri[0] offs=0x2eblock_row_dump:tab 0, row 0, @0x2etl: 4012 fb: -------- lb: 0x2 cc: 1nrid: 0x018047c0.0--链接的块的地址col 0: [4000] 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 ................................省略.....................................end_of_block_dump SQL>select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('018047c0', 'xxxxxxxxxx')) file#, dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('018047c0', 'xxxxxxxxxx')) block# from dual;alter system dump datafile 6 block 18368; System altered 链接块2data_block_dump,data header at 0xa76847c===============tsiz: 0x1f80hsiz: 0x14pbl: 0x0a76847cbdba: 0x018047c0 76543210flag=--------ntab=1nrow=1frre=-1fsbo=0x14fseo=0xfdaavsp=0xfc6tosp=0xfc60xe:pti[0] nrow=1 offs=00x12:pri[0] offs=0xfdablock_row_dump:tab 0, row 0, @0xfdatl: 4006 fb: -----L-- lb: 0x3 cc: 1col 0: [4000] 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33................................省略.....................................end_of_block_dump
3.3小结:行迁移和行迁移的区别、危害
区别:可以看到行链接发生在update或insert,有一部分数据放在当前块,有一部分数据放在链接的块中。行迁移在当前块只放一个地址,内容全部在链接的块中,且可能有多个链接块。
危害:会引起额外的I/O操作。
3.4 如何检测出行迁移?
@?\RDBMS\ADMIN\utlchain.sql analyze table 【table_name】 list chained rows into chained_rows; select owner_name,table_name,head_rowid from chained_rows;
注意:如果要在正式环境上执行,不能在上班时间执行
3.5 如何避免和消除行迁移和行链接
行链接和行迁移检测和消除方法:http://blog.csdn.net/guogang83/article/details/8671425
4.高水位线含义及问题
drop table test1 purge; create table test1 as select * from dba_objects; begin for i in 1 .. 100 loop execute immediate 'insert into test1 select * from dba_objects'; end loop; end; exec dbms_stats.gather_table_stats(user,'TEST1'); select count(*) from test1; delete from test1; select count(*) from test1;
4.1如何修正ORACLE表的高水位线
•执行表重建指令 alter table table_name move;
DDL和DML区别:Metadata(元数据:数据仓库中非常重要的概念,管理数据的数据,oracle中的数据字典据都可以理解成元数据)。DDL就是操作元数据的操作,自动提交。
通过redo及执行时间:
产生redo |
执行时间 |
|
truncate |
53k |
00: 00: 01.06 |
drop |
26k |
00: 00: 00.28 |
用10046跟踪truncate 和drop后台在做什么事情:
update语句数量 |
delete语句数量 |
insert语句数量 |
|
truncate |
7 |
6 |
1 |
drop |
2 |
45 |
0 |
实验5.1 Truncate 与Drop的产生的redo大小
参照:http://blog.csdn.net/guogang83/article/details/8723582测量redo:
create table test as select * from dba_objects;select value 2 from v$mystat, v$statname 3 where v$mystat.statistic# =v$statname.statistic# 4 and v$statname.name ='redo size'; VALUE---------- 94912 truncate table test; Table truncatedselect value 2 from v$mystat, v$statname 3 where v$mystat.statistic# =v$statname.statistic# 4 and v$statname.name ='redo size'; VALUE---------- 149588 select (149588-94912)/1024 from dual; (149588-94912)/1024------------------- 53.39453125 drop table test purge; Table dropped select value 2 from v$mystat, v$statname 3 where v$mystat.statistic# =v$statname.statistic# 4 and v$statname.name ='redo size'; VALUE---------- 168404 create table test as select * from dba_objects; Table created select value 2 from v$mystat, v$statname 3 where v$mystat.statistic# =v$statname.statistic# 4 and v$statname.name ='redo size'; VALUE---------- 263320 drop table test purge; Table dropped select value 2 from v$mystat, v$statname 3 where v$mystat.statistic# =v$statname.statistic# 4 and v$statname.name ='redo size'; VALUE---------- 290364 select (290364-263320)/1024 from dual; (290364-263320)/1024-------------------- 26.41015625
5.2Drop、Truncate 和delete区别--安全性考虑
select * from recyclebin;
6.课后作业:
1.Dump block,你所管理的表,给出内容解析;
row#116[3356] flag: ---D--, lock: 2, len=40
col 0; len 30; (30):
42 49 4e 24 33 53 55 43 46 34 6d 43 52 4e 36 49 78 71 52 34 6d 62 65 64 5a
51 3d 3d 24 30
col 1; len 6; (6): 01 0c 9e 7c 00 2a
-------以上为截取的一段Dump文件数据----
col 0 表示数据
col 1 表示rowid
col 1; len 6; (6): 01 0c 9e 7c 00 2a
16进制----》2进制 00000001 00001100 10011110 01111101 00000000 00101010
OOOOOO FFF BBBBBB RRR