数据块结构
在Oracle在,不论数据块中存储的是表(table)、索引(index)或簇表(clustered data),其内部结构都是类似的。
数据块头(包括标准內容和可变內容)
数据块头(header)中包含了此数据块的概要信息,例如块地址(block address)及此数据块所属的段(segment)的类型(例如,表或索引)。
表目录区
如果一个数据表在此数据块中储存了数据行,那么数据表的信息将被记录在数据块的表目录区(table directory)中。
行目录区
此区域包含数据块中存储的数据行的信息(每个数据行片断(row piece)在行数据区(row data area)中的地址)。〔一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分,所以文中使用row piece〕
当一个数据块(data block)的行目录区(row directory)空间被使用后,即使数据行被删除(delete),行目录区空间也不会被回收。举例来说,当一个曾经包含50条记录的数据块被清空后,其块头(header)的行目录区仍然占用100字节(byte)的空间。只有在数据块中插入(insert)新数据时,行目录区空间才会被重新利用。
管理开销
数据块头(data block header),表目录区(table directory),行目录区(row directory)被统称为管理开销(overhead)。其中有些开销的容量是固定的;而有些开销的总容量是可变的。数据块中固定及可变管理开销的容量平均在84到107字节(byte)之间。
行数据
数据块(data block)中行数据区(row data)包含了表或索引的实际数据。一个数据行可以跨多个数据块。另见:“行链接(Row Chaining)及行迁移(Row Migrating)
可用空间区
在插入新数据行,或在更新数据行需要更多空间时(例如,原来某行最后一个字段为空(trailing null),现在要更新为非空值),将使用可用空间区(free space)中的空间。
如果一个数据块(datablock)属于表或簇表的数据段(data segment),或属于索引的索引段(index segment),那么在其可用空间区中还可能会存储事务条目(transaction entry)。如果一个数据块中的数据行(row)正在由INSERT,UPDATE,DELETE,及SELECT….FORUPDATE语句访问,此数据块中就需要保存事务条目。事务条目所需的存储空间依据操作系统而定。在常见的操作系统中事务条目大约需要占用23字节(byte)。
数据块可用空间的有效性及优化
有两种SQL语句可以增加数据块中的可用空间:分别是DELETE语句,和将现有数据值更新为占用容量更小值的UPDATE语句。在以下两种条件下,上述两中操作释放的空间可以被后续的INSERT语句使用:
- 如果INSERT语句与上述两种操作在同一事务(transaction)中,且位于释放空间的语句之后,那么INSERT语句可以使用被释放的空间。
- 如果INSERT语句与释放空间的语句在不同的事务中(比如两者是由不同的用户提交的),那么只有在释放空间的语句提交后,且插入数据必需使用此数据块时,INSERT语句才会使用被释放的空间。
数据块(data block)中被释放出的空间未必与可用空间区(freespace)相连续。Oracle在满足以下条件时才会将释放的空间合并到可用空间区:
(1)INSERT或UPDATE语句选中了一个有足够可用空间容纳新数据的数据块,(2)但是此块中的可用空间不连续,数据无法被写入到数据块中连续的空间里。Oracle只在满足上述条件时才对数据块中的可用空间进行合并,这样做是为了避免过于频繁的空间合并工作影响数据库性能。
行链接(Row Chaining)及行迁移(Row Migrating)
有两种情况会导致表中某行数据过大,一个数据块(data block)无法容纳。第一种情况,当一行数据被插入时一个数据块就无法容纳。在这种情况下Oracle将这行数据存储在段内的一个数据块链(chain)中。在插入数据量大的行时常会发生行链接(rowchaining),例如一个包含数据类型为LONG或LONGRAW列的数据行。此时行链接不可避免。
第二种情况,原本存储在一个数据块(datablock)内的数据行,因为更新操作导致长度增长,而所在数据块的可用空间也不能容纳增长后的数据行。在这种情况下,Oracle将此行数据迁移(migrate)到新的数据块中。Oracle在被迁移数据行原来所在位置保存一个指向新数据块的指针。被迂移数据行的rowid保持不变。
当数据行发生链接(chain)或迁移(migrate)时,对其访问将会造成Ⅰ/0性能降低,因为Oracle为获取这些数据行的数据时,必须访问更多的数据块(datablock)
PCTFREE,PCTUSED,及行链接(Row Chaining)
在手动管理的表空间(manually managed tablespaces)中,用户可以使用PCTFREE和PCTUSED这两个存储管理参数来控制对某段(segment)进行插入和更新操作时,如何利用属于此段的数据块(data block)中的可用空间。用户也可以在创建或修改索引时为其设定PCTFREE参数(索引存储在索引段(index segment)中)
提示:
并不适用于LOB数据类型(BLOB,CLOB,NCLOB,及BFILE)。这些类型的数据存储时不使用PCTFREE参数及可用块列表(freelist)
PCTFREE参数
PCTFREE参数用来设置一个数据块(datablock)中至少需要保留(reserve)多少可用空间(百分比值),为数据块中已有数据更新时可能发生的数据量增长做准备。例如,当用户用CREATETABLE语句创建表时指定了以下参数:
PCTFREE20
这个参数设定了此表对应的数据段(datasegment)中的每个数据块(data block)至少保留20%的可用空间,以备块中己有数据更新时使用。只要数据块中行数据区与数据块头的容量之和不超过数据块总容量的80%,用户就可以向其中插入新数据,数据行被放入行数据区(rowdataarea),相关信息被写入数据块头(overheadarea)。
PCTUSED参数
PCTUSED参数用于决定一个数据块(datablock)是否可被用于插入新数据,她的依据是数据区(row data)与数据块头(over head)的容量之和占数据块全部容量的最大百分比。当一个数据块中的可用空间比例小于PCTFREE参数的规定时,Oracle就认为此数据块无法被用于插入新数据,直到数据块中的占用容量比例小于PCTUSED参数的限定。在占用容量比例大于PCTUSED参数的限定之前,Oracle只在更新数据块内己有数据时才会使用此数据块的可用空间。例如,当用户用CREATETABLE语句创建表时指定了以下参数:
PCTUSED40
在例子中,当此表的某数据块占用容量比例高于40%时,Oracle不会将此数据块用于插入新数据行(假设此数据块的可用空间曾经低于PCTFREE的限定)。
PCTFREE和OCTUSED如何协同发挥作用
在新分配的数据块中(datablock),可用于插入(insert)数据的空间等于数据块总容量减去数据块头(block over head)再减去预留可用空间(PCTFREE)。而更新(update)数据块内已有数据可使用数据块中的所有可用空间。因此,更新操作能够使数据块內的可用空间低于的PCTFREE限制,因为这些空间是专为更新操作而预留的。
在每个数据段(datasegment)与索引段(index segment)中,Oracle管理着一个或多个可用块列表(free list)一其中列出了所有属于此段的数据扩展(extent),且可用空间比例大于PCTFREE限定的数据块。这些块可以被插入(insert)操作使用。当用户提交了INSERT语句后,Oracle从可用块列表中选择第一个有效的数据块使用。如果此数据块的可用空间不够容纳INSERT语句提交的数据,且此块的占用容量己经超过PCTUSED的限定,Oracle就将其从可用块列表中移出。一个段可以同时使用多个可用块列表,以减少对一个表进行并发插入(concurrent insert)时产生的竞争。
当用户提交了DELETE或UPDATE语句后,Oracle处理语句并检查相关数据块中的占用空间比例是否小于PCTUSED的规定。如果满足,那么这个数据块就被放入当前事务(transaction)正在使用的可用块列表(free list)的头部,如果当前事务还需要写入数据,此块将被首先使用。当事务提交后,此数据块中的可用空间还可被其他事务使用。