本文主要介绍插入(Insert),更新(Update),删除(Delete)操作时数据在数据块(Block)中的变化。首先简单讲一下几个相关的参数。
一.相关参数
在手动管理的表空间(Manually Managed Tablespace)中,用户可以使用PCTFREE和PCTUSED这两个存储管理参数来控制对某段(Segment)进行插入和更新操作时,如何利用属于此段的数据块(Data Block)中的可用空间。用户也可以在创建或修改索引时为其设定PCTFREE参数。
1.PCTFREE
PCTFREE参数用来设置一个数据块中至少需要保留多少可用空间(百分比),为数据块中已有的数据更新世可能发生的行数据长度增长做准备。例如,当用户用CREATE TABLE 语句创建表时指定了PCTFREE为10,那么此表对应的数据段中的每个数据块至少要保留10%的可用空间,以备块中已有数据更新时使用,只要数据块中数据区与数据块头的容量之和不超过1-PCTFREE的值,用户就可以向其中插入新数据。
2.PCTUSED
PCTUSED参数用于决定一个数据块是否可以用户插入新数据,他的依据是数据区域和数据块头的容量之和占数据块全部容量的最大百分比,当一个数据块中的可用空间比例小于PCTFREE参数值时,Oracle认为此数据块无法被用户插入新数据,直到数据块中的数据区域和数据块头之和的比例小于PCTUSED参数值,Oracle才会再次使用这个块来插入新的数据。
3.Freelist与Freelist groups
Freelist与Freelist groups是可插入数据的数据块的列表与列表集。当有新数据插入时,Oracle通过查询此列表找到可以插入数据的数据块。当block剩余空间不足以插入一条记录,同时该block的使用率已经超过PCTUSED定义的值并且该数据块位于freelist header处时,该块也会从freelist上摘走,术语(UNLINK)。当有数据删除(delete)的时候,只有该数据块(block)中的数据被删除到PCTUSED定义的值时,该数据块(block)才会重新被链接(LINK)到freelist中。
4.高水标记(high water mark)
简单来说,HWM就是一个segment中已使用和未使用的数据块(block)的分界线,当请求新的空闲块,并且现有的空闲列表(freelist)中的块不能满足要求时,HWM指向的块将被标记为已使用,然后HWM将移动指向下一个未使用过的块。
Freelist只是管理高水位标志以下的空闲空间,而实际上一个segment可用的空闲空间包括两种类型:已经分配给这个segment但是从来没有使用过的高水位标志之上的blocks。位于高水位标志之下,被链接到freelist上的blocks。下图为数据块的使用示例:
二.插入(Insert),更新(Update),删除(Delete)操作对数据块的影响
创建一个名为test的表,并向里面插入三条记录用于测试。用dba用户登录创建PL/SQL过程show_space,用于显示某个表使用block的信息。show_space的使用参考文章:http://blog.youkuaiyun.com/huang_tg/archive/2010/07/09/5724499.aspx
SQL> create table test
2 (sno varchar2(10),
3 sname varchar2(20));
表已创建。
SQL> insert into test values ('001','huang');
SQL> insert into test values ('002','ting');
SQL> insert into test values ('003','guang');
SQL> commit;
提交完成。
SQL> set serveroutput on
SQL> exec show_space('TEST');
Total Blocks............................3
Total Bytes.............................12288
Unused Blocks...........................1
Unused Bytes............................4096
Last Used Ext FileId....................1
Last Used Ext BlockId...................61573
Last Used Block.........................2
PL/SQL 过程已成功完成。
SQL> alter system dump datafile 1 block 61574;
系统已更改。
data_block_dump
===============
tsiz: 0xfa0
hsiz: 0x18
pbl: 0x0296105c
bdba: 0x0040f086
flag=-------------
ntab=1
nrow=3
frre=-1
fsbo=0x18
fseo=0xf7a
avsp=0xf62
tosp=0xf62
0xe:pti[0] nrow=3 offs=0 --本块存在3条记录
0x12:pri[0] offs=0xf93 --记录的起始物理位置
0x14:pri[1] offs=0xf87
0x16:pri[2] offs=0xf7a
block_row_dump:
tab 0, row 0, @0xf93 --第一行数据开始的物理地址
tl: 13 fb: --H-FL-- lb: 0x1 cc: 2 --lb:表示属于XID 0x1 cc:表示有2字段
col 0: [ 3] 30 30 31 --字段1长度为3,数据30 30 31
col 1: [ 5] 68 75 61 6e 67 --字段2长度为5,数据68 75 51 6e 67
tab 0, row 1, @0xf87
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] 30 30 32
col 1: [ 4] 74 69 6e 67
tab 0, row 2, @0xf7a
tl: 13 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] 30 30 33
col 1: [ 5] 67 75 61 6e 67
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574
1.插入(Insert)数据对数据块的影响
数据在oracle数据库中的存储是从block的底部开始的,遵循从下到上的规则。当一个新行被插入时,oracle首先搜索freelist (数据块中使用的空间未达到PCTUSED设置的值)中的空闲块,如果找到则插入数据。没有找到则使用HWM指向的数据块(block),同时移动HWM,使其指向下一个未使用过的数据块。如果行数据过大,一个数据块装不下,那么就会产生行链接。以下是向测试表中插入两条记录后数据块的变化:
SQL> insert into test values ('004','tang');
SQL> insert into test values ('005','yan');
SQL> commit;
提交完成。
data_block_dump
===============
0xe:pti[0] nrow=5 offs=0 --增加了2条记录
0x12:pri[0] offs=0xf93
0x14:pri[1] offs=0xf87
0x16:pri[2] offs=0xf7a
0x18:pri[3] offs=0xf6e --新增加的004号记录
0x1a:pri[4] offs=0xf63 --新增加的005号记录
block_row_dump:
tab 0, row 3, @0xf6e --第4条记录开始地址
tl: 12 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 3] 30 30 34 --字段1长度为3,数据为30 30 34
col 1: [ 4] 74 61 6e 67 --字段1长度为4,数据为74 61 6e 67
tab 0, row 4, @0xf63 --第5条记录开始地址
tl: 11 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 3] 30 30 35 --字段1长度为3,数据为30 30 35
col 1: [ 3] 79 61 6e --字段1长度为3,数据为79 61 6e
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574
2.删除(delete)数据对数据块的影响
删除(delete):删除数据时,oracle并不回收被删除数据所占用的空间,只是做一个标记以表明,这部分空间可以重用。如果一个数据块因为删除了数据使得他的使用空间低于PCTUSED的值。那么这个数据块将被再次放到freelist中,当需要时就会被重用。一下是删除一条数据以后数据块中的变化:
SQL> delete from test where sno='002' or sno='003';
SQL> commit;
提交完成。
SQL> select * from test;
SNO SNAME
---------- --------------------
001 huang
004 tang
005 yan
SQL> exec show_space('TEST');
Total Blocks............................3
Total Bytes.............................12288
Unused Blocks...........................1
Unused Bytes............................4096
Last Used Ext FileId....................1
Last Used Ext BlockId...................61573
Last Used Block.........................2
PL/SQL 过程已成功完成。
SQL> alter system dump datafile 1 block 61574;
系统已更改。
block_row_dump:
tab 0, row 1, @0xf87 --原002的记录位置,已经没有数据
tl: 2 fb: --HDFL-- lb: 0x1
tab 0, row 2, @0xf7a --原003的记录位置,已经没有数据
tl: 2 fb: --HDFL-- lb: 0x1
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574
SQL> insert into test values ('002','t');
SQL> insert into test values ('003','g');
SQL> commit;
提交完成。
SQL> alter system dump datafile 1 block 61574;
系统已更改。
block_row_dump:
tab 0, row 1, @0xf87 --被删除的002号记录
tl: 2 fb: --HDFL-- lb: 0x1
tab 0, row 2, @0xf7a --被删除的003号记录
tl: 2 fb: --HDFL-- lb: 0x1
tab 0, row 5, @0xf5a --新增加的002号记录
tl: 9 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 3] 30 30 32 --新插入的002号记录的数据
col 1: [ 1] 74
tab 0, row 6, @0xf51 --新增加的003号记录
tl: 9 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 3] 30 30 33 --新插入的003号记录的数据
col 1: [ 1] 67
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574
3.更新(update)数据对数据块的影响
更新(update):数据被更新时,oracle首先使用数据所在数据块(block)所预留的空闲空间(PCTFREE)来存储数据更新可能带来的行数据增大。如果更新产生的新数据过大,造成数据块(block)无法装下新的数据,那么将被拷贝到一个有足够空间的数据块中,而原始块则会存储一个指向更新够新数据的开始地址。以下是更新两条记录以后数据块的变化:
SQL> update test set sname='ting' where sno='002';
SQL> update test set sname='guang' where sno='003';
SQL> commit;
提交完成。
SQL> alter system dump datafile 1 block 61574;
系统已更改。
block_row_dump:
tab 0, row 5, @0xed6 --第5行记录(新插入的002号记录)
tl: 12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] 30 30 32
col 1: [ 4] 74 69 6e 67 --数据已经被更新
tab 0, row 6, @0xec9 --第6行记录(新插入的003号记录)
tl: 13 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] 30 30 33
col 1: [ 5] 67 75 61 6e 67 --数据已经被更新
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574