Oracle存储数据的最小单位是数据块。Oracle管理数据库数据文件的存储空间被称为数据块,一个数据块是数据库使用的最小数据单位。对应的操作系统层面上,所有数据都是以字节形式存储的。每一种操作系统都有自己的块大小。Oracle对数据的请求是Oracle数据块的倍数,而非操作系统块。
标准的块大小是通过初始化参数DB_BLOCK_SIZE指定的,Oracle允许指定最多5种非标准块。为了避免不必要的I/O开销,数据块大小应当是操作系统块大小的整数倍。
关于数据块的几个概念
磁盘扇区大小 512bytes sector size,操作系统空间分配块大小,OS space allocation block size
簇字节数也就是I/O chunk size,通常所说的操作系统块大小
Oracle数据块即DB_BLOCK_SIZE,DB_BLOCK_SIZE应当是操作系统块大小的整数倍数 ,可以有效地避免不必要的I/O,这里的操作系统块指的是簇字节数(I/O chunk size),事实上Oracle的DB_BLOCK_SIZE大小可以小于簇字节数(I/O chunk size),所以Oracle官方文档写的是应当或者建议值。
The standard block size is specified by the initialization parameter DB_BLOCK_SIZE. In addition, you can specify of up to five nonstandard block sizes. The data block sizes should be a multiple of the operating system’s block size within the maximum limit to avoid unnecessary I/O. Oracle data blocks are the smallest units of storage that Oracle can use or allocate.(来自于Oracle Concept)
Oracle日志写入使用512bytes的chunk大小,因此最好把日志放到裸设备和相关硬件上,从而忽略文件系统。(该观点来自于itpub Yong Huang,但是原文出处不可考证),Oracle日志和数据文件写入机制的不同,印证了redo文件最好与其它数据文件进行物理分离。
Since Oracle redo writing uses chunk size of 512 bytes (or 1k on HPUX and a few other OS'es), it's always better to place redo logfiles on raw partitions or equivalent (Veritas Quick I/O, Oracle ASM etc), bypassing filesystems.
正常情况下,可以认为服务器进程读取相应的Oracle块时,会调用Oracle I/O引擎,然后Oracle I/O引擎向操作系统发出请求,操作系统在通过内核进行相应的系统调用接口读取操作系统块;然后文件系统调用相应的块设备进行扇区数据读写。(裸设备暂时无法理解其原理)
参见《关于oracle数据块和操作系统块的关系》
http://www.itpub.net/viewthread.php?tid=1081114&highlight=%CA%FD%BE%DD%BF%E9
chkdsk 可以查看操作系统的块大小(windows平台)
fsutil fsinfo ntfsinfo c: 命令也可查看操作系统的块大小(windows平台)
/sbin/tune2fs -l /dev/sda1 | grep Block 可以查看操作系统的块大小(Linux平台)
show parameter db_block_size 显示Oracle缺省数据块大小
区间是一系列用来存储特定信息的连续的数据块
数据块格式
Oracle数据块的格式无论是表、索引还是cluster数据,格式都是很类似的,见图
块头(公共和变量)
头部包含了通用块信息,例如块的地址和段的类型(例如表或者索引)
表目录
这部分信息包含了在这个块中该表或该索引的相关信息。
行目录
这部分包含了数据块中的实际行的信息(包括行数据区域中每行的地址),一旦数据块头部的这个行目录的空间被分配了,那么即使该行删除了,这段空间仍然不能回收。
因此一个当前为空的数据块
此区域包含数据块中存储的数据行的信息(每个数据行片断(row piece) 在行数据区(row data area)中的地址)。[一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分 ,所以文中使用row piece]。只有在数据块中插入(insert)新数据时,行目录区空间才会被重新利用。
头部信息区(Overhead)
块头(header/Common and Variable),表目录(Table Directory),行目录(Row Directory)这三部分合称为头部信息区(Overhead)。头部信息区不存放数据,它存放的整个块的信息。头部信息区的大小是可变的。一般来说,头部信息区的大小介于84字节(bytes)到107字节(bytes)之间
可用空间(Free Space)
可用空间是一个块中未使用的区域,这片区域用于新行的插入和已经存在的行的更新。可用空间也包含事务条目,当每一次insert、update、delete、select ..for update语句访问块中一行或多行数据,将会请求一条事务条目,事务条目的请求空间与操作系统相关,在多数操作系统中大约所需23个字节。
行数据
这部数据块包含了表或索引的数据,行也可能跨数据块,这也就是行迁移现象。
具体参见:oracle数据文件内部BLOCK结构详解,初始原文已不可考
http://www.sosdb.com/article/datafile_block.htm
可用空间管理(Free Space Management)
可用空间可能是自动或人工管理。
可用空间是由Oracle内部的段自动管理的,段内的可用/已用空间用位图来跟踪。自动段空间管理提供了以下好处:
l 使用便捷
l 更好的空间利用率,特别是那些行大小变化很大的对象
l 并发访问的动态调整
l 性能/空间的平衡
数据块可用空间的利用和压缩
Delete和update(把原值变小)可增加数据块的可用空间。在以下情况下insert语句才能有效地利用已释放的空间。
假如insert语句在同一个事务中,而insert前面的语句刚好释放了相应的空间,这时候insert语句可以利用该空间
假如insert语句与释放空间的语句不在同一个事务中,那么只有当其他事务提交后并且刚好需要空间的时候,insert语句才能利用该空间。
释放的空间也可能不是连续的,只有当1一个insert或update语句试图使用足够空间创建新行的时候,并且2自由空间是分散的以至于不能插入毗邻空间的时候,Oracl才会合并和压缩数据块的可用空间。
行链接和行迁移(Row Chaining and Migrating)
行链接(Row Chaining):如果我们往数据库中插入(INSERT)一行数据,这行数据很大,以至于一个数据块存不下一整行,Oracle就会把一行数据分作几段存在几个数据块中,这个过程叫行链接(Row Chaining)。
如果一行数据是普通行,这行数据能够存放在一个数据块中;如果一行数据是链接行,这行数据存放在多个数据块中。
行迁移(Row Migrating):数据块中存在一条记录,用户执行UPDATE更新这条记录,这个UPDATE操作使这条记录变长,这时候,Oracle在这个数据块中进行查找,但是找不到能够容纳下这条记录的空间,无奈之下,Oracle只能把整行数据移到一个新的数据块。原来的数据块中保留一个“指针”,这个“ 指针”指向新的数据块。被移动的这条记录的ROWID保持不变。
PCTFREE参数用于指定块中必须保留的最小空闲空间百分例。之所以要预留这样的空间,是因为UPDATE时,需要这些空间。如果UPDATE时,没有空余空间,Oracle就会分配一个新的块,这会产生行迁移(Row Migrating)。
PCTUSED也是用于设置一个百分比,当块中已使用的空间的比例小于这个百分比的时候,这个块才被标识为有效状态。只有有效的块才被允许插入数据
创建数据表
create table TEST
(
NAME VARCHAR2(4000)
)
tablespace XXX
pctfree 10 --初始化保留的最小10%的空闲空间比例,低于90%是,均可插入
pctused 40 --当已使用空间比例小于这个百分比时,重新允许插入数据
initrans 1
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
--插入测试数据,并观察其相应的rowid
insert into test values('1');
insert into test values('2');
insert into test values('3');
commit;
select t.*,t.rowid from test t;
update test set name=lpad(name,4000,'1') where name='1';
update test set name=lpad(name,4000,'2') where name='2';
update test set name=lpad(name,4000,'3') where name='3';
commit;
select t.*,t.rowid from test t;
--创建数据字典表chained_rows
@F:\OracleForVista\product\10.2.0\db_1\RDBMS\ADMIN\utlchain.sql;
--分析相关表
ANALYZE TABLE test LIST CHAINED ROWS;
--查询链接行或迁移行
SELECT * FROM CHAINED_ROWS where table_name='TEST';
select t.*,t.rowid from test t;
行迁移解决办法:其实没有太好的办法,只能是行迁移数据删除后,重新插入回来
Create table new_table as select * from old_table;
Drop table old_table;
Rename new_table to old_table;
Normal
0
7.8 磅
0
2
false
false
false
EN-US
ZH-CN
X-NONE
image002.jpg


