表簇是一组表,它们共享公共的列,并将相关的数据存储在相同的数据块中。当表被聚簇时,单个数据块可以包含多个表中的行。例如,一个块可以同时存储来自employees表和departments表的行,而不只是单个表中的行。
簇键是所有被聚簇的表的共有列或列集。例如,employees表和departments表共享department_id列。您在创建表簇时,和创建被添加到表簇的每个表时,指定簇键。
簇键值是一组特定行的簇键列的值。包含相同簇键值的所有数据(例如department_id=20),物理上存储在一起。每个簇键值在簇或簇索引中只存储一次,而无论在这些不同表中有多少行包含这个值。
打个比方,假设一个人力资源经理有两个书柜:一个书柜装有一盒盒雇员文件夹,而另一个书柜装有一盒盒部门文件夹。用户经常要找一个特定部门的所有员工的文件夹。为便于检索,经理重新排列了书柜中的所有盒子。她将这些盒子按部门id划分。这样,部门20的所有雇员文件夹和该部门20的文件夹本身放在一个盒子中;部门100的所有雇员文件夹和该部门100的文件夹本身放在另一个盒子中,依次类推。
如果多个表主要是被查询(而不是修改),且各表中的记录是经常被一起查询或联接,在这些情况下可以考虑将他们聚簇化。因为表簇将不同表中的相关行存储在同一个数据块中,被正确使用的表簇相比非聚簇表具有下列优点:
•对于被聚簇表的联接,可以减少磁盘I/O。
•对于被聚簇表的联接,可以提高访问速度。
•只需更少的空间来存储相关的表和索引数据,因为簇键值不会为每行重复存储。
通常,簇表不适合以下情况:
•会经常被更新的表。
•经常需要全表扫描的表。
•需要被截断的表。
索引化表簇概述
索引化聚簇是使用索引来查找数据的表簇。而簇索引是一个簇键上的B树索引。簇索引必须先被创建,然后才能将行插入到簇表中。
假定使用簇键department_id来创建聚簇employees_departments_cluster,如例2-8所示。因为未指定HASHKEYS子句,这个簇是一个索引化表簇。然后,在这个簇键上创建一个名为idx_emp_dept_cluster的索引
例2-8索引化聚簇

然后在该簇中创建雇员表和部门表,并指定department_id列为簇键,如下所示(省略号表示放置列定义的地方)

最后,将行添加到employees表和departments表中。数据库在物理上将雇员表和部门表中每个部门的所有行都存储在相同的数据块中。数据库以堆的形式存储行,并使用索引定位他们。
图2-5显示了包含employees表和departments表所构成的employees_departments_cluster表簇。数据库将部门20的雇员所在的行存储在一起,部门110的行也存储在一起,依次类推。如果表未被聚簇,那么数据库将不保证相关的行被存储在一起。
图2-5簇表数据

B树簇索引把簇键值与数据所在块的数据库块地址(DBA)关联起来。例如,键20的索引条目显示包含部门20的雇员数据所在块的地址:
20,AADAAAA9d
与非聚簇表上的索引类似,簇索引被单独管理,簇索引也可以与表簇存在于不同的表空间中。
哈希簇概述
除了索引键被替换为一个哈希函数之外,哈希簇就像一个索引化聚簇。它没有单独的簇索引存在。对一个哈希簇来说,数据本身就是索引。
对已索引的表或索引化聚簇,数据库用存储在一个单独的索引中的键值查找表行。要查找或存储已索引的表或索引化聚簇中的一个行,数据库必须执行至少两个I/O操作:
•为查找或存储在索引中的键值,需要一个或多个I/O
•为读取或写入表或表簇中的行,还需要一个I/O
为查找或存储在哈希簇中的一个行,数据库将哈希函数应用到行的簇键值。得出的哈希值对应到一个聚簇中的数据块,数据库则按发出的语句读写该块。
哈希是一种可选的表数据存储方法,用来提高数据检索的性能。在满足以下条件时,哈希簇可能是有益的:
•经常被查询,但不经常被修改的表。
•哈希键列经常使用等值条件查询,例如,WHEREdepartment_id=20。对于这样的查询,簇键值是已经过哈希运算的。哈希键值直接指向存储行的磁盘区域。
•您可以合理地猜出哈希键的数目,和每个键值所存储的数据的大小。
创建哈希簇
与索引化聚簇的键类似,簇键是由簇中各表共享的单键列或复合键列。哈希键值是插入到簇键列的实际值或可能值。例如,如果簇键是department_id,那么哈希键键值可能是10、20、30,等等。
Oracle数据库使用一个哈希函数,接受任意多个哈希键值作为输入,经过排序并哈希到有限数量的桶。每个桶都有一个唯一的数字ID,称为哈希值。每个哈希值都映射到哈希键值(部门10、20、30,等等)对应行所在块的数据库块地址。
与创建索引化聚簇类似,使用CREATECLUSTER语句创建一个哈希簇,但得加上一个哈希键。哈希值的数量取决于哈希键。示例2-9中可能存在的部门的数量是100,所以HASHKEYS设置为100。
示例2-9哈希簇

employees表和departments表。然后可以将数据装载到哈希簇中,如同示例2-8中所述的索引化簇一样。
查询哈希簇
由数据库(而不是用户)确定如何哈希用户输入的键值。例如,假设用户经常执行如下的查询,为p_id输入不同的部门ID号:

如果某个用户查询department_id=20的雇员,那么数据库可能将此值哈希到桶77。如果某个用户查询department_id=10的雇员,那么数据库可能将此值哈希到桶15。数据库使用内部生成的哈希值来定位包含请求的部门的雇员行所在的数据块。
图2-6将哈希簇段显示为一行水平排列的块。如图所示,查询只需单个I/O就可以检索到数据。
Figure2-6RetrievingDatafromaHashCluster

哈希簇的一个限制是在非索引化簇键上的范围扫描不可用(请参见"索引范围扫描")。假设在示例2-9中没有为哈希簇创建单独的索引。对部门id在20和100之间的查询不能使用哈希算法,因为它不能哈希20和100之间的每个可能的值。因为没有索引存在,数据库必须执行完全扫描。
哈希簇变体
单表哈希簇是哈希簇的一个优化版本,一次只支持一个表。哈希键和行之间存在一一映射。当用户需要通过主键快速访问表时,单表哈希簇会很有用。例如,用户经常通过employee_id查找一个雇员表中的雇员记录。
排序哈希簇存储哈希函数的每个值对应的行,通过某种方式,数据库可以有效地把他们按已排定的顺序返回。数据库在内部执行优化的排序。对于需要总是按排定顺序来消费数据的应用程序,这种技术可能会更快的检索到数据。例如,应用程序可能总是按订单表的order_date列进行排序。
哈希簇存储
Oracle数据库为哈希簇分配空间的方式不同于索引化簇。在示例2-9中,HASHKEYS指定可能存在的部门数,而SIZE指定与每个部门相关联的数据的大小。则数据库将基于以下公式计算存储空间:

因此,如果示例2-9中的块大小是4096字节,那么数据库至少要为该哈希簇分配200个数据块。
Oracle数据库并不限制您可以向哈希簇中插入的哈希键值的数目。例如,即使HASHKEYS是100,但这不会阻止你向部门表中插入200个不同的部门。但是,当(输入的)哈希键值的数目超过哈希键(HASHKEYS的设定)数目时,哈希簇检索效率会降低。
为说明检索问题,假定图2-6中的块100内全是部门20的行。用户向部门表中插入一个新的部门,其department_id为43。部门的数量超过HASHKEYS的值,因此数据库对department_id43进行哈希处理得到哈希值77,这与department_id20的哈希值相同。多个输入的值被哈希为相同的输出值称为哈希冲突。
当用户为部门43往簇中插入行时,数据库不能将这些行存储在块100中,因为它已经满了。数据库将块100链接到一个新的溢出块,比如说块200,并将插入的行存储在新块中。这两个块100和200现在都可以存储部门20或43的数据。如图2-7,查询部门20或43现在需要两个i/o才能检索到数据:块100和其相关联的块200。您可以通过重新创建具有不同HASHKEYS值的表簇来解决这个问题。
图2-7当哈希冲突发生时,从哈希簇检索数据





