Oracle数据块详解
操作系统块是操作系统读写的最小操作单元,也是操作系统文件的属性之一。当创建一个Oracle数据库时,选择一个基于操作系统块的整数倍大小作为Oracle数据库块的大小。Oracle数据库读写操作则是以Oracle块为最小单位,而非操作系统块。
数据库块也称逻辑块或Oracle块,它对应磁盘上一个或多个物理块,它的大小由初始化参数DB_BLOCK_SIZE决定,可以定义数据块为2K、4K、8K、16K、32K甚至更大,默认Oracle块大小是8K。若一旦设置了Oracle数据块的大小,则在整个数据库生命期间不能被更改。使用一个合适的Oracle块大小对于数据库的调优是非常重要的。
OS在每次执行I/O的时候是以OS的块为单位;Oracle在每次执行I/O的时候是以Oracle块为单位。Oracle块具有以下特点:
① 最小的I/O单元;
② 包含一个或多个OS块;
③ 大小由参数DB_BLOCK_SIZE决定;
④ 数据库创建时设置,数据库创建后不能更改。
在Oracle中,不论数据块中存储的是表(TABLE)、索引(INDEX)或簇表(CLUSTER TABLE),其内部结构都是类似的。Oracle块的结构如下图所示:
图 3-8 Oracle 块结构图
由上图可以看出,一个Oracle块大约由数据块头(包括标准内容和可变内容,Common And Variable Header)、表目录(Table Directory)、行目录(Row Directory)、可用空间(Free Space)和行数据(Row Data)这几部分组成。图中两个箭头表示一个数据块中的可用空间区的容量是可变的。
●数据块头(Block Header):主要包含有数据块地址的一些基本信息(块地址,Block Address)和段的类型(表段、索引段等)。块头自上而下进行增长。
●表目录(Table Directory):如果一个堆组织表在此数据块中储存了数据行,那么该表的信息将被记录在数据块的表目录中。多个表可以将行存储在相同的块中。
●行目录(Row Directory):此区域包含数据块中存储的数据行的信息,即每个数据行片断(Row Piece)在行数据(Row Data)中的地址。一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分。当一个数据块(Data Block)的行目录(Row Directory)空间被使用后,即使数据行被删除(DELETE),行目录空间也不会被回收。举例来说,当一个曾经包含50条记录的数据块被清空后,其块头(Header)的行目录仍然占用100字节(Byte)的空间。仅在块中插入新行时,数据库才会重用此空间。
●可用空间(Free Space):是指可以为以后的更新和插入操作分配的空间,大小由PCTFREE和PCTUSED两个参数影响。可用空间位于块的中部,允许头和行数据空间在必要时进行增长。当插入新行或用更大的值更新现有行的列时,行数据会占用可用空间。导致块头增长的事件包括:行目录需要更多的行条目和需要的事务处理插槽数多于最初配置的数目。块中的可用空间最初是相邻的。但是,删除和更新操作可能会使块中的可用空间变成碎片。
●行数据(Row Data):数据块(Data Block)中行数据(Row Data)包含了表或索引的实际数据。一个数据行可以跨多个数据块。行数据空间自下而上进行增长。
下面介绍一下与数据块存储相关的几个概念。
1、 什么是ASSM和MSSM?
段空间有两种管理方式,分别是手动段空间管理(Manual Segment Space Management,MSSM)和自动段空间管理(Auto Segment Space Management,ASSM)。
自动段空间管理(ASSM),它首次出现在Oracle 9.2中。自由列表FREELIST被位图所取代,它是一个二进制的数组,能够迅速有效地管理存储扩展和剩余区块(Free Block),因此能够改善分段存储本质。ASSM会忽略PCTUSED参数而只使用PCTFREE参数。对于MSSM而言,可以设置FREELIST、PCTUSED和PCTFREE等参数来控制如何分配和使用段中的空间。
2、 FREELIST(自由列表)
Oracle通过维护FREELIST列表来记录或更新所有可用的数据块。当执行INSERT语句时,Oracle首先在FREELIST列表上搜索可用的空闲数据块,搜索成功之后将数据插入到那个空闲块。块在FREELIST列表中的可用性由PCTFREE参数值来决定。起初一个空块在FREELIST列表上列出,并且会一直保留,直到空闲空间达到PCTFREE设定的值。当一个块被使用且达到PCTFREE设定的值之后,该块将从FREELIST列表被移除,而当数据块的可用空间低于PCTUSED值的时候,该块又会回收,即重新回到FREELIST列表。Oracle使用FREELIST方式以提高数据库性能。因此,每一个INSERT操作,Oracle仅仅需要搜索FREELIST结构,而不是搜索所有数据块。从Oracle 9i开始,引入了ASSM,它让Oracle自动管理FREELIST。在ASSM里,Oracle使用位图方式来标记数据块是否可用,这种数据块的空间管理方式比用一个列表来管理效率更高。
3、 PCTFREE(空闲率)和PCTUSED(使用率)
PCTFREE和PCTUSED这两个参数是面试中常问到的概念。它们用来优化数据块空间的使用,控制数据块是否出现在FREELIST中。当创建或者更改任何表和索引的时候,Oracle在空间控制方面使用这两个存储参数。
●PCTFREE:块中保留用于UPDATE操作的空间百分比,当数据占用的空间达到此上限时,新的数据将不能再插入到此块中。当数据块中的FREE空间小于PCTFREE设置的空间时,该数据块从FREELIST中去掉,当块由于DML操作FREE空间大于PCTUSED设置的空间时,该数据库块将被添加在FREELIST链表中。对于表和索引来说,该值默认为10%,通过查询DBA_TABLES或DBA_INDEXES视图的PCT_FREE列可以获取到该属性的值。该值适用于MSSM和ASSM。
●PCTUSED:指定块中数据使用空间的最低百分比;用于为插入一新行数据的最小空间的百分比。这个值决定了块的可用状态。可用状态的块可以执行插入操作,不可用状态的块只能执行删除和修改,可用状态的块被放在FREELIST中。该值只针对表有效,默认值为40%,通过查询DBA_TABLES视图的PCT_USED列可以获取到该属性的值。该值仅适用于MSSM。
若要修改表的PCTFREE和PCTUSED的值,可以使用ALTER语句修改。需要注意的是,修改之后只对新块起作用,若要对表中原有的块起作用,则可以使用MOVE重新组织表,SQL语句如下所示:
ALTER TABLE T_TEST_LHR PCTFREE 20;
ALTER TABLE T_TEST_LHR MOVE;
若要修改索引的PCTFREE的值,可以使用如下的SQL语句:
ALTER INDEX PK_TEST_LHR REBUILD PCTFREE 20;
下面给出一个示例:
SYS@lhrdb> CREATE TABLE TEST_BLOCK_LHR(
2 COL1 CHAR(20),
3 COL2 NUMBER) ;
Table created.
SYS@lhrdb> CREATE INDEX IND_COL1_LHR ON TEST_BLOCK_LHR(COL1);
Index created.
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.PCT_USED,
3 T.INI_TRANS
4 FROM DBA_TABLES T
5 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE PCT_USED INI_TRANS
---------- ---------- ----------
10 40 1
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.ini_trans
3 FROM DBA_INDEXES T
4 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE INI_TRANS
---------- ----------
10 2
SYS@lhrdb> ALTER TABLE TEST_BLOCK_LHR PCTFREE 20 PCTUSED 60;--修改表的PTCFREE和PCTUSED的值
Table altered.
SYS@lhrdb> SELECT T.PCT_FREE,
2 T.PCT_USED,
3 T.INI_TRANS
4 FROM DBA_TABLES T
5 WHERE T.TABLE_NAME ='TEST_BLOCK_LHR';
PCT_FREE PCT_USED INI_TRANS
---------- ---------- ----------
20 60 1
SYS@lhrdb> ALTER TABLE TEST_BLOCK_LHR MOVE;--重新组织表
Table altered.
SYS@lhrdb> ALTER INDEX IND_COL1_LHR REBUILD PCTFREE 20;--修改索引的PTCFREE值
Index altered.
下面详细介绍一下数据库块的dump结构。首先准备如下的表:
CREATE TABLE T_TESTBLOCK_20160926_LHR( ID NUMBER, NAME VARCHAR(4) ) TABLESPACE TS_TESTBLOCKLHR;
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (1, 'a');
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (2, 'b');
INSERT INTO T_TESTBLOCK_20160926_LHR VALUES (3, 'c');
COMMIT;
SYS@lhrdb> SELECT ROWID,DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) REL_FNO,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BLOCKNO FROM T_TESTBLOCK_20160926_LHR;
ROWID REL_FNO BLOCKNO
------------------ ---------- ----------
AAAXh8AAHAAAACDAAA 7 131
AAAXh8AAHAAAACDAAB 7 131
AAAXh8AAHAAAACDAAC 7 131
可以看到这3行数据都在7号数据文件的第131个块。将该数据块dump出来:
SYS@lhrdb> ALTER SYSTEM DUMP DATAFILE 7 BLOCK 131;
System altered.
SYS@lhrdb> SELECT VALUE FROM V$DIAG_INFO WHERE NAME LIKE '%Default%';
VALUE
--------------------------------------------------------------------------------
/oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_12386484.trc
以下是该数据块的完整dump结果:
[LHRDB1:oracle]:/oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace>more lhrdb_ora_12386484.trc
Trace file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_12386484.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options
ORACLE_HOME = /oracle/app/oracle/product/11.2.0/db
System name: AIX
Node name: LHRDB1
Release: 1
Version: 7
Machine: 00F63A614C00
Instance name: lhrdb
Redo thread mounted by this instance: 1
Oracle process number: 30
Unix process pid: 12386484, image: oracle@LHRDB1 (TNS V1-V3)
*** 2016-09-26 11:33:32.934
*** SESSION ID:(190.1277) 2016-09-26 11:33:32.934
*** CLIENT ID:() 2016-09-26 11:33:32.934
*** SERVICE NAME:(SYS$USERS) 2016-09-26 11:33:32.934
*** MODULE NAME:(sqlplus@LHRDB1 (TNS V1-V3)) 2016-09-26 11:33:32.934
*** ACTION NAME:() 2016-09-26 11:33:32.934
*** TRACE FILE RECREATED AFTER BEING REMOVED ***
Start dump data blocks tsn: 8 file#:7 minblk 131 maxblk 131
Block dump from cache:
Dump of buffer cache at level 4 for tsn=8 rdba=29360259
BH (0x700010023ff51d0) file#: 7 rdba: 0x01c00083 (7/131) class: 1 ba: 0x700010023ee4000
set: 19 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 41,22
dbwrid: 0 obj: 96380 objn: 96380 tsn: 8 afn: 7 hint: f
hash: [0x7000100627ed2a0,0x7000100627ed2a0] lru: [0x700010023ff6640,0x700010023ff5a10]
ckptq: [NULL] fileq: [NULL] objq: [0x70001005dfd2cf0,0x70001005dfd2cf0] objaq: [0x70001005dfd2ce0,0x70001005dfd2ce0]
st: XCURRENT md: NULL fpin: 'ktspbwh2: ktspfmdb' tch: 2
flags: block_written_once redo_since_read
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
Block dump from disk:
buffer tsn: 8 rdba: 0x01c00083 (7/131)
scn: 0x0000.00752951 seq: 0x01 flg: 0x06 tail: 0x29510601
frmt: 0x02 chkval: 0x4235 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0000000110ADCC00 to 0x0000000110ADEC00
110ADCC00 06A20000 01C00083 00752951 00000106 [.........u)Q....]
110ADCC10 42350000 01000007 0001787C 00752950 [B5........x|.u)P]
110ADCC20 00000000 00023200 01C00080 00080002 [......2.........]
110ADCC30 000009E9 00C0108B 04AC2400 20030000 [..........$. ...]
110ADCC40 00752951 00000000 00000000 00000000 [.u)Q............]
110ADCC50 00000000 00000000 00000000 00000000 [................]
110ADCC60 00000000 00010003 FFFF0018 1F801F65 [...............e]
110ADCC70 1F650000 00031F90 1F881F80 00000000 [.e..............]
110ADCC80 00000000 00000000 00000000 00000000 [................]
Repeat 501 times
110ADEBE0 00000000 2C010202 C1040163 2C010202 [....,......c,...]
110ADEBF0 C1030162 2C010202 C1020161 29510601 [...b,......a)Q..]
Block header dump: 0x01c00083
Object id on Block? Y
seg/obj: 0x1787c csc: 0x00.752950 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1c00080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.002.000009e9 0x00c0108b.04ac.24 --U- 3 fsc 0x0000.00752951
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
bdba: 0x01c00083
data_block_dump,data header at 0x110adcc64
===============
tsiz: 0x1f98
hsiz: 0x18
pbl: 0x110adcc64
76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f80
avsp=0x1f65
tosp=0x1f65
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1f90
0x14:pri[1] offs=0x1f88
0x16:pri[2] offs=0x1f80
block_row_dump:
tab 0, row 0, @0x1f90
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: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
tab 0, row 2, @0x1f80
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [ 1] 63
end_of_block_dump
End dump data blocks tsn: 8 file#: 7 minblk 131 maxblk 131
下面对该dump内容做详细解说:
一、块头信息区
首先看头部信息区分析:
Block dump from disk:
buffer tsn: 8 rdba: 0x01c00083 (7/131)
scn: 0x0000.00752951 seq: 0x01 flg: 0x06 tail: 0x29510601
frmt: 0x02 chkval: 0x4235 type: 0x06=trans data
① buffer tsn: 8 表示该块对应的表空间号,这里是8号表空间
② rdba: 0x01c00083 (7/131) 其中,rdba(relative data block address)表示相对数据块地址,其中(7/131)表示该块为7号数据文件第131个块,用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。01c00083(十六进制)=0000 0001 1100 0000 0000 0000 1000 0011(二进制),不难看出前10位(0000 0001 11)转换成十进制就是7,后22位(00 0000 0000 0000 1000 0011)转换成十进制就是131。rdba在数据块中的offset是4,即rdba存在于数据块中的第5-9字节中(offset从0开始算),数据块中的每个部分在数据块中的偏移量通过BBED可以展示出来
③ scn: 0x0000.00752951 表示数据块头部SCN,总共占用6个字节,前2个字节(0000)表示SCN Wrap,后4个字节(00752951)表示SCN Base。如果SCN Base达到了4个字节表示的最大值,SCN Wrap+1,SCN Base清0。在数据块中的offset是8。这里的SCN号为7678289
④ seq: 0x01 表示Sequence number即日志序列号。在数据块中的offset是14
⑤ flg: 0x06 flg即Flag,其中,0x01代表New block即新建块;0x02代表Delayed Logging Change advanced SCN即数据块延迟清洗推进scn和seq;0x04代表Check value即设置校验和;0x08代表Temporary block即临时块。其它值都是01、02、04、08的组合。在数据块中的offset是15
⑥ tail: 0x29510601 即tail check,存放于数据块的最后4个字节,用于数据块一致性检查。tail check的组成:SCN Base的低2个字节+type+seq。即tail:0x32d30601=32d3+06+01
⑦ frmt: 0x02 代表块格式。01表示Oracle 7,02表示Oracle 8+
⑧ chkval: 0x4235 代表块检查值。如果参数DB_BLOCK_CHECKSUM=TRUE,那么数据块在读入buffer和写回数据文件之前都要做检查计算,如果计算值和数据块中记录的计算值不匹配就会标记该块是坏块
⑨ type: 0x06=trans data 代表块类型,参考以下的表格:
ID |
Type |
01 |
Undo segment header |
02 |
Undo data block |
03 |
Save undo header |
04 |
Save undo data block |
05 |
Data segment header (temp, index, data and so on) |
06 |
KTB managed data block (with ITL) |
07 |
Temp table data block (no ITL) |
08 |
Sort Key |
09 |
Sort Run |
10 |
Segment free list block |
11 |
Data file header |
二、事务列表区
Block header dump: 0x01c00083
Object id on Block? Y
seg/obj: 0x1787c csc: 0x00.752950 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1c00080 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.002.000009e9 0x00c0108b.04ac.24 --U- 3 fsc 0x0000.00752951
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
①、Object id on Block? Y 表示该块是否属于某个对象
②、seg/obj: 0x1787c 表示该数据块中对象的OBJECT_ID。本示例dump的是表T_TESTBLOCK_20160926_LHR,下面来验证一下:
SYS@lhrdb> select to_number('1787c','xxxxxx') from dual;
TO_NUMBER('1787C','XXXXXX')
---------------------------
96380
SYS@lhrdb> select object_name,object_type from dba_objects where object_id=96380;
OBJECT_NAME OBJECT_TYPE
---------------------------- -------------------
T_TESTBLOCK_20160926_LHR TABLE
③、csc: 0x00.752950 表示SCN at last Block CleanOut,表示最后一次块清除(Block CleanOut)时候的SCN
④、itc: 2 块中ITL slot的数量,根据下面的ITL图可以看出的确只有2个ITL slot
⑤、flg: E 0表示此块被放置在自由列表(freelist)中,E指用的是ASSM
⑥、typ: 1 DATA 表示数据库块的类型,1表示数据,2表示索引
⑦、bdba: 0x1c00080 Block relative data block address,相对数据块的地址
接下来的内容属于ITL的范围:
图 3-9 ITL图
ITL(Interested Transaction List,事务槽)是Oracle数据块内部的一个组成部分,用来记录在该块上发生的所有事务。1个ITL可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个ITL的位置就可以被反复使用了,因为LTL与记录非常类似,所以,有的时候也叫ITL槽位。
ITL位于数据块头(Block Header),ITL事务槽由槽位号(Itl)、Xid(Transaction ID)、Uba(Undo Block Address)、Flag、Lck和Scn/Fsc几个部分组成。Oracle的每个数据块中都有一个或者多个事务槽,每一个对数据块的并发访问事务都会占用一个事务槽。对于已经提交的事务,ITL槽位不会马上被覆盖,因为一致性读可能会用到这个信息,一致性读的时候,可能需要从这里获得回滚段的入口,并从回滚段中获得一致性读。
当发出一条SQL语句时,Oracle会记录下这个时刻的SCN,然后在Buffer Cache中查找需要的BLOCK,或者从磁盘上读。当别的会话修改了数据,或者正在修改数据时,就会在相应的块上记录ITL,此时Oracle发现ITL中记录的SCN大于SELECT时刻的SCN,那么Oracle就会根据ITL中的Uba找到Undo信息获得该BLOCK的前镜像,然后在Buffer Cache中构造出CR(consistent read)块,此时Oralce也会检查构造出来的BLOCK中ITL记录的SCN。如果SCN还大于SELECT时刻的SCN,那么一直重复构造前镜像,然后Oracle找到前镜像BLOCK中的ITL的SCN是否小于SELECT的SCN,同时检查这个事务有没有提交或者回滚。如果没有,那么继续构造前镜像,直到找到需要的BLOCK,如果在构造前镜像的过程中所需的Undo信息被覆盖了,就会报快照过旧的错误。
如果一个事务一直没有提交,那么这个事务将一直占用一个ITL槽位。如果这个事务已经提交,那么,ITL槽位中还保存的有这个事务提交时候的SCN号。
ITL的个数受参数INITRANS控制,最大ITL个数受MAXTRANS控制(Oracle 10g已废弃MAXTRANS,默认最大支持255个并发)。在一个块内部,默认分配了2个ITL的个数。如果这个块内还有空闲空间(Free Space),那么Oracle是可以利用这些空闲空间并再次分配ITL。如果没有了空闲空间,那么这个块会因为不能分配新的ITL可能发生ITL等待,即enq: TX - allocate ITL entry等待事件。
在并发量特别大的系统中,最好分配足够的ITL个数,或者设置足够的PCTFREE,保证ITL能扩展。但是,PCTFREE有可能是被行数据给消耗掉的,如UPDATE,所以,也有可能导致块内部的空间不够而导致ITL等待。对于表(数据块)来说,INITRANS这个参数的默认值是1。对于索引(索引块)来说,这个参数默认值是2。
下面详细介绍ITL的结构:
① Itl:ITL事务槽编号,ITL事务槽号的流水编号
② Xid:代表对应的事务id(transac[X]tion identified),在回滚段事务表中有一条记录和这个事务对应。Xid由3列使用十六进制编码的数字列表示,分别是:Undo Segment Number + Transaction Table Slot Number + Wrap,即由undo段号+undo槽号+undo槽号的覆盖次数三部分组成,即usn.slot.sqn,这里0x0008.002.000009e9转换为10进制为8.2.2537,从下边的查询出的结果是相对应的:
SYS@lhrdb> SELECT XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBASQN,UBAREC FROM V$TRANSACTION;
XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN UBAREC
---------- ---------- ---------- ------- ---------- -------- ----------
8 2 2537 3 4235 1196 36
③ Uba:即Undo Block Address,该事务对应的回滚段地址,记录了最近一次的该记录的前镜像(修改前的值)。Uba组成:Undo块地址(undo文件号和数据块号)+回滚序列号+回滚记录号。多版本一致读是Oracle保证读操作不会被事务阻塞的重要特性。当Server Process需要查询一个正在被事务修改,但是尚未提交的数据时,就根据ITL上的Uba定位到对应Undo前镜像数据位置。这里的Uba为:0x00c0108b.04ac.24,其中00c0108b(16进制)=0000 0000 1100 0000 0001 0000 1000 1011(2进制,共32位,前10位代表文件号,后22位代表数据块号)=文件号为3,块号为4235(10进制);04ac(16进制)=1196(10进制);24(16进制)=36(10进制)。Uba的值可以从V$TRANSACTION查询出来:
SELECT UBAFIL 回滚段文件号,UBABLK 数据块号,UBASQN 回滚序列号,UBAREC 回滚记录号 FROM v$transaction ; --查看UBA
④ Flag:事务标志位,即当前事务槽的状态信息。这个标志位记录了这个事务的操作状态,各个标志的含义分别是:
标识 |
简介 |
---- |
事务是活动的,未提交,或者在块清除前提交事务。 |
C--- |
事务已经提交,锁已经被清除(提交)。 |
-B-- |
包含ITL记录的UNDO数据。 |
--U- |
事务已经提交,但是锁还没有清除(快速提交)。 |
---T |
块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,那么在clean ount的时候,块会被进行清除,但是这个块里面的事务不会被清除。 |
C-U- |
块被延迟清除,回滚段的信息已经改写,SCN显示为最小的SCN,需要由回滚段重新生成,例如在提交以前,数据块已经刷新到数据文件上。 |
⑤ Lck:表示这个事务所影响的行数,锁住了几行数据,对应有几个行锁。在这里,可以看到01号事务槽Lck为3,因为该事务槽中的事务Flag为U,证明该事务已经提交,但是锁还没有清除。再比如对于下边这个ITL:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.020.00000271 0x00800205.0257.13 C--- 0 scn 0x0000.001732c4
0x02 0x0008.006.00000279 0x00800351.0278.15 ---- 1 fsc 0x0000.00000000
看到01号事务槽Lck为0,因为该事务槽中的事务Flag为C,证明该事务已经提交,锁已经被清除,该事务槽可以被重用。02号事务槽Lck为1,是因为对第一行做了一个更新操作,并且没有提交,Flag为“----”说明该事务是活动的。
⑥ Scn/Fsc:Scn表示提交时的SCN。Commit SCN或者快速提交(Fast Commit,Fsc)的SCN。Scn=SCN of commited TX;Fsc=Free space credit(bytes)。每条记录中的行级锁对应Itl条目lb,对应于Itl列表中的序号,即那个事务在该记录上产生的锁。
对于一个Oracle事务来说,可以是快速提交、也可以是延迟提交,目的都是为了提高提交的速度。提交以后,Oracle需要对ITL事务槽、每一行的锁定标记进行清除。如果是快速提交,那么在提交的时候,会将事务表和每一个数据块的ITL槽进行清除。但是锁定标记可能没有清除,等下次用到的时候再进行清除。如果是延迟提交,那么在提交的时候,只是将事务表进行清除,并没有对ITL事务槽进行清除,每一行的锁定标记也没有清除。因此,C和U的情况特别多。块清除的过程并不包括每个行的锁定标记的清除,主要指的是ITL的清除。
注意:
① 事务槽中首先记录的是XID和UBA,只有在提交以后,当对这个数据块进行CLEANOUT的时候,才会更新FLAG和SCN。因此,Oracle总是以事务表中对这个数据块的SCN以及FLAG为准。
② 一个事务开始以后,在一个数据块上得到一个事务槽,那么在这个事务提交以前,这个事务槽会一直占用,直到这个事务提交才会释放这个事务槽。
③ 只有在已经提交以后,这个ITL事务槽中的SCN才会有数值。
④ 事务是否已经提交、事务对应的SCN,这些信息都是以回滚段事务表中的为主,事务槽中的不准确。
⑤ 事务槽中的事务ID和UBA地址是准确的。
⑥ 事务槽中的事务ID和回滚段中的事务ID肯定不是一样的,不同回滚段中的事务ID也一定不一样。
三、用户数据头区(data_block_dump)
行目录(Row Directory)记录了数据块里每一行相对于起点的偏移量,Oracle正是通过行目录找到所需的数据行。
bdba: 0x01c00083
data_block_dump,data header at 0x110adcc64
===============
tsiz: 0x1f98
hsiz: 0x18
pbl: 0x110adcc64
76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f80
avsp=0x1f65
tosp=0x1f65
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1f90
0x14:pri[1] offs=0x1f88
0x16:pri[2] offs=0x1f80
① bdba: 0x01c00083 block dba/rdba(数据块地址),用4个字节32位来表示,前10位为相对数据文件号,后22位为块号。01c00083(十六进制)=0000 0001 1100 0000 0000 0000 1000 0011(二进制),可以看到前10位(0000 0001 11)转换成十进制就是7,后22位(00 0000 0000 0000 1000 0011)转换成十进制就是131,即7号文件131号数据块
② tsiz: 0x1f98 Total Data Area Size(数据区的大小,块的总大小),转换为10进制即8088字节
③ hsiz: 0x18 数据块头大小,转换为10进制即24字节
④ pbl: 0x110adcc64 指向这个数据块在内存中映像的指针
⑤ flag=-------- N=pcrfree hit(clusters);F=do not put on free list;K=flushable cluster keys
⑥ ntab=1 number of tables (>1 is a cluster)
⑦ nrow=3 即行数,这里表示这个表有3行数据
⑧ frre=-1 first free row index entry, -1=you have to add one(没有创建索引)
⑨ fsbo=0x18 free space begin offset(空闲空间起始位置),叫起始空间:可以存放数据空间的起始位置(即定义了数据层中空闲空间的起始offset)
⑩ fseo=0x1f80 free space end offset(空闲空间结束位置),叫结束空间:可以存放数据空间的结束位置(即定义了数据层中空闲空间的结束offset)
? avsp=0x1f65 available space in the block(可用空间),叫空闲空间:定义了数据层中空闲空间的字节数
? tosp=0x1f65 total available space when all txs commit,叫最终空闲空间:定义了ITL中事务提交后,数据层中空闲空间的字节数
? 0xe:pti[0] nrow=3 offs=0 Table directory,整个表的开始,该块有3条记录
? 0x12:pri[0] offs=0x1f5e 第1条记录在偏移量为0x1f5e的地方,下面两行以此类推
? 0x14:pri[1] offs=0x1f66
? 0x16:pri[2] offs=0x1f80
四、用户数据区(block_row_dump)
block_row_dump:
tab 0, row 0, @0x1f90
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: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
tab 0, row 2, @0x1f80
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [ 1] 63
end_of_block_dump
① tab 0, row 0, @0x1f90 第一个表第一行的位置,定义了该表在行索引中的起始插槽号
② lb: 0x1 表示lock byte。锁定该行的这个事务在ITL的入口,0x1说明事务在该数据行上的锁还没清除,并且该锁指向01号事务槽。lb: 0x0说明事务在该数据行上的锁已经被清除
③ tl: 8 表示Row Size(number of bytes plus data)
④ fb Flag Byte
K- Cluster key
H- head of row piece
D- Deleted row
F- first data piece
L- last data piece
P- First column cintinues from previous row N- Last column cintinues in next piece
当DELETE一行数据的时候,数据并不是物理地被删除,而是把该行标记为删除,这个时候fb应该是--HDFL--而不是原来的--H-FL--。
⑤ cc 表示number of columns in this Row piece
关于行中的数据,可以以第一行来说明一下。由于表的第2行数据为(2,'b'),所以可以使用dump函数来验证一下。dump函数可以按指定的格式显示输入数据的内部表示,这里显示16进制:
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
SYS@lhrdb> select dump(2,16),dump('b',16) from dual;
DUMP(2,16) DUMP('B',16)
----------------- ----------------
Typ=2 Len=2: c1,3 Typ=96 Len=1: 62
原文地址:http://blog.csdn.net/yujin2010good/article/details/7747204
数据库的逻辑结构包括:数据块,区,段,表空间。
oracle数据块是oracle数据库存储基础,有磁盘空间的若干字节组成,数据块是oracle数据库的最小逻辑单元,可以定义数据块为2k、4k、8k、16k、32k甚至更大,默认oracle块大小是8k,通常我们称为oracle块。当然正常情况下oracle块应该是os块的整数倍,当然具有标准大小的块叫做标准块,和标准块不同的块叫做非标准块。同一个数据库中,当然指的是9i以后,支持同一个数据中同时使用标准块和非标准块。
Oracle块大小设置在初始化参数文件里面(init.ora)中的db_block_size中设置,块是处理update、insert、select数据事务的最小单位,当用户从表中选择数据时,将在oracle块上读取或是提取数据。意思就是说:os每次执行i/o时候,是以os的块为单位;oracle每次执行i/o时候,是以oracle块为单位。
总结为一下几点:
? 最小的 I/O单元
? 包含一个或多个 OS块
? DB_BLOCK_SIZE
? 数据库创建时设置,数据库创建后不能更改
Oracle数据块的大小设置意见:
1、 如果行较小且访问随机,则选择小的块尺寸
2、 如果行小且访问连续的,或者有较大的行,则选择较大尺寸的块。
就是说看我们的业务系统。
数据块结构
1、基本组成
块结构说明如下:
块头:存放一些基本信息,如物理位置,块所属的段类型(数据段、索引段、回滚段等)
表目录:如果块中存储的数据为表数据,则表目录中保存这个表的相关信息
行目录:如果块中存储的数据为表数据,则行目录中保存数据行的相关信息。
块头、表目录和行目录组成块的头部信息(overhead),这部分不存数据库中实际的数据,而是用来记录该块的逻辑结构,而且这部分占用的空间不是固定的,大约在84-107字节之间。
行记录:真正存放数据的区域,这部分空间已被使用。
空闲空间:未使用的区域,用于新行的插入或者已经存在行的更新。(这里有个概念行迁移:就是当update操作时,块中空闲的空间不够保存修改的数据,那么记录就将保存到另一个拥有足够空间的块中,而只在原块上保留一条指向新块的rowid,这就是行迁移row migration)
2、自由空间
空闲空间的使用
Insert这时候块的自由空间会减少,
update的时候块的自由空间也会减少
当使用DELETE语句删除块中的记录或者使用UPDATE语句把列的值更改成一个更小值的时候,Oracle会释放出一部分自由空间。释放出的自由空间并不一定是连续的,常情况下,Oracle不会对块中不连续的自由空间进行合并。因为合并数据块中不连续的自由空间会影响数据库的性能。只有当用户进行数据插入(INSERT)或者更新(UPDATE)操作,却找不到连续的自由空间的时候,Oracle才会合并数据块中不连续的自由空间。
3、空闲空间的管理
对于块中的自由空间,Oracle提供两种管理方式:自动管理,手动管理
行链接和行迁移(Row Chaining and Migrating)
行链接(Row Chaining):如果我们往数据库中插入(INSERT)一行数据,这行数据很大,以至于一个数据块存不下一整行,Oracle就会把一行数据分作几段存在几个数据块中,这个过程叫行链接(Row Chaining)。
如果一行数据是普通行,这行数据能够存放在一个数据块中;如果一行数据是链接行,这行数据存放在多个数据块中。
行迁移(Row Migrating):数据块中存在一条记录,用户执行UPDATE更新这条记录,这个UPDATE操作使这条记录变长,这时候,Oracle在这个数据块中进行查找,但是找不到能够容纳下这条记录的空间,无奈之下,Oracle只能把整行数据移到一个新的数据块。原来的数据块中保留一个“指针”,这个“指针”指向新的数据块。被移动的这条记录的ROWID保持不变。行迁移的原理如下图所示:
无论是行链接还是行迁移,都会影响数据库的性能。Oracle在读取这样的记录的时候,Oracle会扫描多个数据块,执行更多的I/O。而且是成倍加大i/o。
1)Oracle使用位图(bitmap)来管理和跟踪数据块,这种块的空间管理方式叫“自动管理”。自动管理有下面的好处:
◆易于使用
◆更好地利用空间
◆可以对空间进行实时调整
2)块中自由空间的手动管理(手动管理比较复杂)
用户可以通过PCTFREE, PCTUSED来调整块中空间的使用,这种管理方式叫手动管理。相对于自动管理,手动管理方式比较麻烦,不容易掌握,容易造成块中空间的浪费。
PCTFREE参数用于指定块中必须保留的最小空闲空间百分例。之所以要预留这样的空间,是因为UPDATE时,需要这些空间。如果UPDATE时,没有空余空间,Oracle就会分配一个新的块,这会产生行迁移(Row Migrating)。
PCTUSED也是用于设置一个百分比,当块中已使用的空间的比例小于这个百分比的时候,这个块才被标识为有效状态。只有有效的块才被允许插入数据。
三、基本实验
1)下面我们先来分析一下块。
下面我们来看看这个数据块
dump说明
创建表空间和测试表:
create tablespace testblock datafile '/opt/app/oracle/oradata/wolf/testblock01.dbf' size 100M;
create table testblock(
id number,
name varchar(4)
) tablespace testblock;
插入3条数据然后提交:
SQL> insert into testblock values(1,'a');
SQL> insert into testblock values(2,'b');
SQL> insert into testblock values(3,'c');
SQL> commit;
SQL> select * from testblock;
ID NAME
---------- --------
1 a
2 b
3 c
SQL> commit
SQL> desc V$datafile;
Name Null? Type
----------------------------------------- -------- ----------------------------
FILE# NUMBER
CREATION_CHANGE# NUMBER
CREATION_TIME DATE
TS# NUMBER
RFILE# NUMBER
STATUS VARCHAR2(7)
ENABLED VARCHAR2(10)
CHECKPOINT_CHANGE# NUMBER
CHECKPOINT_TIME DATE
UNRECOVERABLE_CHANGE# NUMBER
UNRECOVERABLE_TIME DATE
LAST_CHANGE# NUMBER
LAST_TIME DATE
OFFLINE_CHANGE# NUMBER
ONLINE_CHANGE# NUMBER
ONLINE_TIME DATE
BYTES NUMBER
BLOCKS NUMBER
CREATE_BYTES NUMBER
BLOCK_SIZE NUMBER
NAME VARCHAR2(513)
PLUGGED_IN NUMBER
BLOCK1_OFFSET NUMBER
AUX_NAME VARCHAR2(513)
FIRST_NONLOGGED_SCN NUMBER
FIRST_NONLOGGED_TIME DATE
SQL> select FILE#,name from v$datafile;
FILE#
----------
NAME
--------------------------------------------------------------------------------
1
/opt/app/oracle/oradata/wolf/system01.dbf
2
/opt/app/oracle/oradata/wolf/undotbs01.dbf
3
/opt/app/oracle/oradata/wolf/sysaux01.dbf
FILE#
----------
NAME
--------------------------------------------------------------------------------
4
/opt/app/oracle/oradata/wolf/users01.dbf
5
/opt/app/oracle/oradata/wolf/testblock01.dbf
SQL> select rowid,dbms_rowid.rowid_relative_fno(rowid) rel_fno,dbms_rowid.rowid_block_number(rowid)
blockno from testblock;
2
ROWID REL_FNO BLOCKNO
------------------ ---------- ----------
AAAMixAAFAAAAAMAAA 5 12
AAAMixAAFAAAAAMAAB 5 12
AAAMixAAFAAAAAMAAC 5 12
SQL> alter system dump datafile 5 block 12;
SQL> show parameter dump;
查找dumping文件位置
[root@test ~]# cd /opt/app/oracle/admin/wolf
[root@test wolf]# ls
adump bdump cdump dpdump pfile udump
[root@test wolf]# cd udump/
[root@test udump]# ls
wolf_ora_18566.trc wolf_ora_19120.trc wolf_ora_4952.trc wolf_ora_5792.trc
wolf_ora_18605.trc wolf_ora_19790.trc wolf_ora_4954.trc wolf_ora_5826.trc
wolf_ora_18608.trc wolf_ora_19818.trc wolf_ora_5721.trc wolf_ora_5833.trc
wolf_ora_19087.trc wolf_ora_19820.trc wolf_ora_5764.trc wolf_ora_5860.trc
wolf_ora_19114.trc wolf_ora_4899.trc wolf_ora_5766.trc wolf_ora_9291.trc
[root@test udump]# ls -l
total 272
-rw-r----- 1 oracle oinstal● 593 Ju● 8 05:56 wolf_ora_18566.trc
-rw-r----- 1 oracle oinstal● 650 Ju● 8 05:56 wolf_ora_18605.trc
-rw-r----- 1 oracle oinstal● 1872 Ju● 8 06:05 wolf_ora_18608.trc
-rw-r----- 1 oracle oinstal● 593 Ju● 8 04:05 wolf_ora_19087.trc
-rw-r----- 1 oracle oinstal● 650 Ju● 8 04:05 wolf_ora_19114.trc
-rw-r----- 1 oracle oinstall 196171 Ju● 8 04:21 wolf_ora_19120.trc
-rw-r----- 1 oracle oinstal● 593 Ju● 8 06:29 wolf_ora_19790.trc
-rw-r----- 1 oracle oinstal● 650 Ju● 8 06:29 wolf_ora_19818.trc
-rw-r----- 1 oracle oinstal● 3331 Ju● 8 11:08 wolf_ora_19820.trc
-rw-r----- 1 oracle oinstal● 619 Jul 14 05:25 wolf_ora_4899.trc
-rw-r----- 1 oracle oinstal● 648 Jul 14 05:25 wolf_ora_4952.trc
-rw-r----- 1 oracle oinstal● 2717 Jul 14 05:36 wolf_ora_4954.trc
-rw-r----- 1 oracle oinstal● 591 Ju● 7 10:02 wolf_ora_5721.trc
-rw-r----- 1 oracle oinstal● 907 Ju● 7 10:03 wolf_ora_5764.trc
-rw-r----- 1 oracle oinstal● 591 Ju● 7 10:03 wolf_ora_5766.trc
-rw-r----- 1 oracle oinstal● 2630 Ju● 7 10:03 wolf_ora_5792.trc
-rw-r----- 1 oracle oinstal● 776 Ju● 7 10:03 wolf_ora_5826.trc
-rw-r----- 1 oracle oinstal● 591 Ju● 7 10:03 wolf_ora_5833.trc
-rw-r----- 1 oracle oinstal● 648 Ju● 7 10:04 wolf_ora_5860.trc
-rw-r----- 1 oracle oinstal● 2045 Ju● 8 04:04 wolf_ora_9291.trc
[root@test udump]# date
Sat Jul 14 05:38:57 EDT 2012
[root@test udump]# cat wolf_ora_4954.trc | more
/opt/app/oracle/admin/wolf/udump/wolf_ora_4954.trc
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
ORACLE_HOME = /opt/app/oracle/product/10g
System name: Linux
Node name: test
Release: 2.6.18-128.el5
Version: #1 SMP Wed Dec 17 11:42:39 EST 2008
Machine: i686
Instance name: wolf
Redo thread mounted by this instance: 1
Oracle process number: 15
Unix process pid: 4954, image: oracle@test (TNS V1-V3)
*** 2012-07-14 05:36:52.410
*** SERVICE NAME:(SYS$USERS) 2012-07-14 05:36:52.410
*** SESSION ID:(159.3) 2012-07-14 05:36:52.410
Start dump data blocks tsn: 6 file#: 5 minblk 12 maxblk 12
buffer tsn: 6 rdba: 0x0140000c (5/12)
scn: 0x0000.0007e543 seq: 0x05 flg: 0x02 tail: 0xe5430605
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0CEF4200 to 0x0CEF6200
CEF4200 0000A206 0140000C 0007E543 02050000 [......@.C.......]
CEF4210 00000000 00000001 0000C8B1 0007E52C [............,...]
CEF4220 00000000 00320002 01400009 00230007 [......2...@...#.]
CEF4230 000000DD 0080077F 003400CC 00002003 [..........4.. ..]
CEF4240 0007E543 00000000 00000000 00000000 [C...............]
CEF4250 00000000 00000000 00000000 00000000 [................]
CEF4260 00000000 00030100 0018FFFF 1F651F80 [..............e.]
CEF4270 00001F65 1F900003 1F801F88 00000000 [e...............]
CEF4280 00000000 00000000 00000000 00000000 [................]
Repeat 501 times
CEF61E0 00000000 0202012C 630104C1 0202012C [....,......c,...]
CEF61F0 620103C1 0202012C 610102C1 E5430605 [...b,......a..C.]
Block header dump: 0x0140000c
Object id on Block? Y
seg/obj: 0xc8b1 csc: 0x00.7e52c itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400009 ver: 0x01 opc: 0
inc: 0 exflg: 0
It● Xid Uba Flag Lck Scn/Fsc
0x01 0x0007.023.000000dd 0x0080077f.00cc.34 --U- 3 fsc 0x0000.0007e543
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
data_block_dump,data header at 0xcef4264
===============
tsiz: 0x1f98
hsiz: 0x18
pbl: 0x0cef4264
bdba: 0x0140000c
76543210
flag=--------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0x1f80
avsp=0x1f65
tosp=0x1f65
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1f90
0x14:pri[1] offs=0x1f88
0x16:pri[2] offs=0x1f80
block_row_dump:
tab 0, row 0, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
co● 0: [ 2] c1 02
co● 1: [ 1] 61
tab 0, row 1, @0x1f88
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
co● 0: [ 2] c1 03
co● 1: [ 1] 62
tab 0, row 2, @0x1f80
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
co● 0: [ 2] c1 04
co● 1: [ 1] 63
end_of_block_dump
End dump data blocks tsn: 6 file#: 5 minblk 12 maxblk 12
一些参数需要自己去了解
Oracle数据块实现原理深入解读
原文:http://www.jb51.net/article/31929.htm下午在学习oracle 10g r2 concepts
在这留一笔。
Oracle对数据库数据文件(datafile)中的存储空间进行管理的单位是数据块(data block)。数据块是数据库中最小的(逻辑)数据单位。与数据块对应的,所有数据在操作系统级的最小物理存储单位是字节(byte)。每种操作系统都有一个被称为块容量(block size)的参数。Oracle每次获取数据时,总是访问整数个(Oracle)数据块,而不是按照操作系统块的容量访问数据。
数据库中标准的数据块(data block)容量是由初始化参数 DB_BLOCK_SIZE指定的。除此之外,用户还可以指定五个非标准的数据块容量(nonstandardblock size)。数据块容量应该设为操作系统块容量的整数(同时小于数据块容量的最大限制),以便减少不必要的I/O操作。Oracle数据块是Oracle可以使用和分配的最小存储单位。
数据块结构
在Oracle中,不论数据块中存储的是表(table)、索引(index)或簇表(clustered data),其内部结构都是类似的。块结构图如下所示:
本图显示了数据块的各个组成部分,包括:数据块头(包括标准内容和可变内容)(common and variable header),表目录区(table directory),行目录区(row directory),可用空间区(free space),行数据区(row data)。图中两个箭头表示一个数据块中的可用空间区的容量是可变的。
数据块头(包括标准内容和可变内容)
数据块头(header)中包含了此数据块的概要信息,例如块地址(block address)及此数据块所属的段(segment)的类型(例如,表或索引)。
表目录区
如果一个数据表在此数据块中储存了数据行,那么数据表的信息将被记录在数据块的表目录区(table directory)中。
行目录区
此区域包含数据块中存储的数据行的信息(每个数据行片断(row piece) 在行数据区(row data area)中的地址)。[一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分 ,所以文中使用row piece]
当一个数据块(data block)的行目录区(row directory)空间被使用后,即使数据行被删除(delete),行目录区空间也不会被回收。举例来说,当一个曾经包含50条记录的数据块被清空后,其块头(header)的行目录区仍然占用100字节(byte)的空间
管理开销
数据块头(data block header),表目录区(table directory),行目录区(rowdirectory)被统称为管理开销(overhead)。其中 有些开销的容量是固定的;而有些开销的总容量是可变的。数据块中固定及可变管理开销的容量平均在84到107字节(byte)之间。
行数据
数据块(data block)中行数据区(row data)包含了表或索引的实际数据。一个数据行可以跨多个数据块。
可用空间区
在插入新数据行,或在更新数据行需要更多空间时(例如,原来某行最后一个字段为空(trailing null),现在要更新为非空值),将 使用可用空间区(free space)中的空间。如果一个数据块(data block)属于表或簇表的数据段(data segment),或属于索引的索引段(index segment),那么在其可用空间区中还可能会存储事务条目(transaction entry)。如果一个数据块中的数据行(row)正在由INSERT,UPDATE,DELETE,及 SELECT...FOR UPDATE 语句访问,此数据块中就需要保存事务条目。事务条目所需的存储空间依据操作系统而定。在常见的操作系统中事务条目大约需要占用23字节(byte)。
可用空间管理
可用空间可以被手动或自动管理
数据库中,每个段(segment)的可用空间可以被自动管理。段内的可用/已用空间以位图(bitmap)形式记录,这与可用块以列表方式的管理不同。段空间自动管理(Automatic segment-space management)具备以下优势:
● 易于使用● 空间利用效率更高,尤其针对每行数据容量差异大的表(或其他对象)● 能够更好地针对当前数据的情况实时调整● Better multi-instance behavior. in terms of performance/space utilization
用户可以在创建一个本地管理的表空间(locally managed tablespace)时选择自动段空间管理(automatic segment-space management)功能。这样在此表空间内创建的段都将默认地设置为自动段空间管理。
数据块可用空间的有效性及优化
有两种SQL语句可以增加数据块中的可用空间:分别是 DELETE 语句,和将现有数据值更新为占用容量更小值的 UPDATE 语句。在以下两种条件下,上述两中操作释放的空间可以被后续的 INSERT 语句使用:
● 如果 INSERT 语句与上述两种操作在同一事务(transaction)中,且位于释放空间的语句之后,那么 INSERT 语句可以使用被释放的空间。● 如果 INSERT 语句与释放空间的语句在不同的事务中(比如两者是由不同的用户提交的),那么只有在释放空间的语句提交后,且插入数据必需使用此数据块时,INSERT 语句才会使用被释放的空间。
数据块(data block)中被释放出的空间未必与可用空间区(free space)相连续。Oracle在满足以下条件时才会将释放的空间合并到可用空间区:(1)INSERT 或 UPDATE 语句选中了一个有足够可用空间容纳新数据的数据块,(2)但是此块中的可用空间不连续,数据无法被写入到数据块中连续的空间里。Oracle只在 满足上述条件时才对数据块中的可用空间进行合并,这样做是为了避免过于频繁的空间合并工作影响数据库性能。
行链接(Row Chaining)及行迁移(Row Migrating)
有两种情况会导致表中某行数据过大,一个数据块(data block)无法容纳。第一种情况,当一行数据被插入时一个数据块就无法容纳。在这种情况下Oracle将这行数据存储在段内的一个数据块链(chain)中。在插入数据量大的行时常会发生行链接(row chaining),例如一个包含数据类型为 LONG 或LONG RAW 列的数据行。此时行链接不可避免。
第二种情况,原本存储在一个数据块(data block)内的数据行,因为更新操作导致长度增长,而所在数据块的可用空间也不能容纳增长后的数据行。在这种情况下,Oracle将此行数据迁移(migrate)到新的数据块中。Oracle在被迁移数据行原来所在位置保存一个指向新数据块的指针。被迁移数据行的rowid 保持不变。
当数据行发生链接(chain)或迁移(migrate)时,对其访问将会造成 I/O 性能降低,因为Oracle为获取这些数据行的数据时,必须访问更多的数据块(data block)。
PCTFREE,PCTUSED,及行链接(Row Chaining)
在手动管理的表空间(manually managed tablespaces)中,用户可以使用PCTFREE 和 PCTUSED 这两个存储管理参数来控制对某段(segment)进行插入和更新操作时,如何利用属于此段的数据块(data block)中的可用空间。用户也可以在创建或修改索引时为其设定 PCTFREE 参数(索引存储在索引段(index segment)中)
PCTFREE 参数
PCTFREE 参数用来设置一个数据块(data block)中至少需要保留(reserve)多少可用空间(百分比值),为数据块中已有数据更新时可能发生的数据量增长做准备。例如,当用户用 CREATE TABLE 语句创建表时指定了以下参数:
PCTFREE 20
这个参数设定了此表对应的数据段(data segment)中的每个数据块(datablock)至少保留20%的可用空间,以备块中已有数据更新时使用。只要数据块中行数据区与数据块头的容量之和不超过数据块总容量的80%,用户就可以向其中插入新数据,数据行被放入行数据区(row data area),相关信息被写入数据块头(overhead area)
下图说明了PCTFREE 的作用:
PCTUSED 参数
PCTUSED 参数用于决定一个数据块(data block)是否可被用于插入新数据,她的依据是数据区(row data)与数据块头(overhead)的容量之和占数据块全部容量的最大百分比。当一个数据块中的可用空间比例小于PCTFREE 参数的规定时,Oracle就认为此数据块无法被用于插入新数据,直到数据块中的占用容量比例小于 PCTUSED 参数的限定。在占用容量比例大于 PCTUSED 参数的限定之前,Oracle只在更新数据块内已有数据时才会使用此数据块的可用空间。例如,当用户用 CREATE TABLE 语句创建表时指定了以下参数:
PCTUSED 40
在例子中,当此表的某数据块占用容量比例高于40%时,Oracle不会将此数据块用于插入新数据行(假设此数据块的可用空间曾经低于 PCTFREE 的限定)
PCTUSED 的作用
PCTFREE 和 PCTUSED 如何协同发挥作用
PCTFREE 和 PCTUSED 共同作用可以优化数据块(data block)的空间使用。
使用 PCTFREE 和 PCTUSED 参数管理数据块的可用空间
上图说明了 PCTFREE 和 PCTUSED 如何共同作用以管理数据块(data block)可用空间的使用。
在第一步中,数据块占用空间比例小于80%时才能插入新数据,因为PCTFREE 参数限定必须保留20%的可用空间用于块内已有数据的更新。在第二步中,对数据块中已有数据的更新操作可以使用数据块中的保留空间。只有当数据块内的占用空间比例低于40%时才能向其中插入新数据。在第三步中,当数据块内的占用空间比例低于40%时,此数据块再次可以被用于插入新数据。在第四步中,数据块占用空间比例小于80%时才能插入新数据,因为PCTFREE 参数限定必须保留20%的可用空间用于块内已有数据的更新。此过程如此往复循环。
在新分配的数据块中(data block),可用于插入(insert)数据的空间等于数据块总容量减去数据块头(block overhead)再减去预留可用空间(PCTFREE)。而更新(update)数据块内已有数据可使用数据块中的所有可用空间。因此,更新操作能够使数据块内的可用空间低于的 PCTFREE 限制,因为这些空间是专为更新操作而预留的。
在每个数据段(data segment)与索引段(index segment)中,Oracle管理着一个或多个可用块列表(free list)--其中列出了所有属于此段的数据扩展(extent),且可用空间比例大于 PCTFREE 限定的数据块。这些块可以被插入(insert)操作使用。当用户提交了 INSERT 语句后,Oracle从可用块列表中选择第一个有效的数据块使用。如果此数据块的可用空间不够容纳 INSERT语句提交的数据,且此块的占用容量已经超过PCTUSED 的限定,Oracle就将其从可用块列表中移出。一个段可以同时使用多个可用块列表,以减少对一个表进行并发插入(concurrent insert)时产生的竞争。
当用户提交了 DELETE 或 UPDATE 语句后,Oracle处理语句并检查相关数据块中的占用空间比例是否小于 PCTUSED 的规定。如果满足,那么这个数据块就被放入当前事务(transaction)正在使用的可用块列表(free list)的头部,如果当前事务还需要写入数据,此块将被首先使用。当事务提交后,此数据块中的可用空间还可被其他事务使用。
Oracle读取和修改数据块的过程
Oracle读取和修改数据块的过程
Oracle数据库处理SQL都会经过三个过程:解析(parse)、执行(exec)、返回结果(fetch)。为了便于理解,我们首先省略的SQL的处理过程,而直接介绍数据块的读取或修改过程。
物理读和逻辑读概念
1.对要访问的块地址做HASH
HASH(FILE#,BLOCK#) = hash_value(HV)
2.根据HV找到对应的HASH bucket
3.搜索bucket中的HASH chain链表,比对chain上的buffer_header(BH).
逻辑读
1.找到BH后,读取BH中的(buffer_address)BA
2.根据BA访问buffer_cache中的目标buffer.
物理读
1.搜遍hash_chain上的BH都没有找到目标BH
2.从DBF中加载block到buffer中.
3.并将buffer头链入对应的BUCKET中的hash chain上.
4.访问buffer.
latch和pin
buffer_cache中的buffer对每个server_process而已都是公有资源谁都可以读,谁都可以改,为保证这些链不被并发修改导致破坏。oracle采用latch和buffer_pin锁来控制对内存结构体使用时的并发。
CBC latch
某个服务器进程在chain上搜索BH时,另一个进程要做入链出链的操作,对于一个buffer,两个进程同时都要去做出入链,内存则没有安全保证。为保证这些链不被并发修改导致破坏
例如:我在搜索链表上的buffer_header时,你不能更改链表,我在更改链表时,你不能遍历链表
oracle对hash_chain必须加以保护。 oracle采取的保护方式就是latch(闩锁),确切的说是:cache buffer chain latch,简称为: CBC latch。
buffer pin
当在hash_chain上找到目标BH时,根据访问方式要对BH加锁.才能根据(buffer_address)BA访问buffer。这把上在buffer_header上的锁称之为buffer pin
共享与独占
无论是CBC latch还是buffer pin都有锁的保持模式。他们都有共享和独占模式,对公有资源而言,有独占就有争用。
CBC latch何时共享持有:
1.读非唯一索引的根块+分支块
注意:读非唯一索引的叶子块是排他.
2.通过唯一索引访问时,索引的根块+分支块+叶子块+表块
原因:因为根块和分支块的访问频度高,而且很少改动。
CBC latch何时独占持有:
1.所有涉及改的操作。
2.以及上面列出的共享持有以外的所有读操作.
通过上面的比较是否更深入的理解 INDEX UNIQUE SCAN 和 INDEX RANGE SCAN建索引能唯一就唯一,绝对不会错。
获取CBC latch的目的是什么?
1.在hash chain上的双向链表搜索BH
2.在BH中修改buffer pin锁的状态
buffer pin何时共享持有:通过BH中的BA读buffer
buffer pin何时独占持有:通过BH中的BA写buffer
buffer pin何时无需持有:
1.所有不涉及索引的根块+分支块改动的读(分裂)
2.唯一索引叶子块+表块
BH上的队列:
users list对buffer操作的进程列表,兼容模式操作
waiter list因访问模式不兼容,等待队列中的用户
再给大家进一步介绍Hash Latches、HashBucket 、buffer Header及Buffer cache之间的关系图。
Oracle读取或修改数据块的流程:
一.(读操作)非唯一索引方式,非索引根块和分支块
1. 对要访问的块地址做HASH
HASH(FILE#,BLOCK#) = hash_value(HV)
2. 根据HV找到对应的HASH bucket
3. 以独占方式申请管辖此bucket的CBC latch
4. 搜索bucket中的HASH chain链表,比对chain上的buffer_header(BH),找到目标BH
5. 修改BH中的buffer pin锁为S(share)模式(之前为0,无锁)
6. 释放CBC latch.
7. 根据BH中的BA指针找到buffer
8. 读取buffer中的数据到PGA中.
9. 以独占方式申请管辖此bucket的CBC latch
10.修改BH中的buffer pin锁为0(无锁)模式
11.释放CBC latch
二.(读操作)唯一索引方式所有块,非唯一索引根块和分支块
1. 对要访问的块地址做HASH
HASH(FILE#,BLOCK#) = hash_value(HV)
2. 根据HV找到对应的HASH bucket
3. 以共享方式申请管辖此bucket的CBC latch
4. 搜索bucket中的HASH chain链表,比对chain上的buffer_header(BH),找到目标BH
5. 根据BH中的BA指针找到buffer
6. 读取buffer中的数据到PGA中
7. 释放CBC latch
三.(写操作)
1. 对要访问的块地址做HASH
HASH(FILE#,BLOCK#) = hash_value(HV)
2. 根据HV找到对应的HASH bucket
3. 以独占方式申请管辖此bucket的CBC latch
4. 搜索bucket中的HASH chain链表,比对chain上的buffer_header(BH),找到目标BH
. 5. 修改BH中的buffer pin锁为X(exclusive)模式(之前为0,无锁)
6. 释放CBC latch
7. 根据BH中的BA指针找到buffer
8. 产生redo数据
8. 修改buffer中的数据.
9. 以独占方式申请管辖此bucket的CBC latch.
10.修改BH中的buffer pin锁为0模式
11.释放CBC latch.
>
>
>
>
>
>
>
>
>
|
|
><
>
>
><
>
>
>
><
>
>
>
>