聚簇并不是有序地存储数据(这是IOT的工作),它是按每个键以聚簇方式存储数据,但数据存储在堆中。利用聚簇,一个块可能包含多个表的数据。聚簇也可以用于单个表,可以按某个列将数据分组存储。
SQL Server 中的聚簇索引(clustered index)则要求行按索引键有序的方式存储,这类似于前面所述的IOT。
例如,有部门和员工2张表,将部门号作为聚簇列,建立聚簇表。这样部门号10的部门数据和包括的所有员工数据都存储在同一个块上,如果放不下,则串链其他块。
但是部门在磁盘上的存储并不是有序的,部门100可能挨在部门1旁边,而与部门101和99离得很远。
创建索引聚簇表(index clustered table)的步骤:
1)创建聚簇
SIZE选项告诉Oracle与每个聚簇键值关联大约2048字节的数据,Oracle会使用这个参数来计算每个块最多能放下多少个聚簇键。假设块大小为8KB,Oracle会在每个数据库块上放上最多3个聚簇键。
对于聚簇来说,SIZE是最重要的参数,这是对聚簇空间利用率影响最大的因素。如果把这个SIZE设置得太高,那么每个块上的键就会很少,会不必要地使用更多的空间。如果设置得太低,又会导致数据过分串链。
对象的存储定义参数(PCTFREE、PCTUSED、INITRANS等)与 CLUSTER相关,而不是与表相关。因为聚簇中会有多个表,而且它们在同一个块上。
2)创建聚簇键索引
create index emp_dep_cluster_idx on cluster emp_dep_cluster;也可以在聚簇中创建完表格后再创建,但是必须在插入数据前创建,否则会得到下述错误。
ORA-02032: clustered tables cannot be used before the cluster index is built
聚簇索引就像是一个常规的索引,使用和常规同样的的存储参数,也可以存储在另一个表空间中,也可以在多列上建立。
注意,CREATE INDEX语句中,并没有指定列的一个列表,索引列可以由CLUSTER定义本身得出。
3)创建表格
create table department ( depno number(2) primary key, depname varchar2(20)) cluster emp_dep_cluster(depno); create table employee ( empno number primary key, empname varchar2(20), depno number(2) references department(depno)) cluster emp_dep_cluster(depno);
需要使用CLUSTER关键字来指定基表的哪个列会映射到聚簇本身的聚簇键。
聚簇中的表使用创建聚簇时生成的聚簇段,不会有诸如TABLESPACE、PCTFREE等段属性。
在表格中插入一些数据,然后观察在磁盘上的存放。
tony@ORA11GR2> select rowid, dbms_rowid.rowid_block_number(rowid) block, depno from department; ROWID BLOCK DEPNO ------------------ ---------- ---------- AAASIEAAEAAADN0AAA 13172 10 AAASIEAAEAAADN0AAB 13172 20 AAASIEAAEAAADN0AAC 13172 30 AAASIEAAEAAADN1AAA 13173 40 tony@ORA11GR2> select rowid, dbms_rowid.rowid_block_number(rowid) block, empno, depno from employee; ROWID BLOCK EMPNO DEPNO ------------------ ---------- ---------- ---------- AAASIEAAEAAADN0AAA 13172 7369 20 AAASIEAAEAAADN0AAB 13172 7566 20 AAASIEAAEAAADN0AAC 13172 7654 30 AAASIEAAEAAADN0AAD 13172 7698 30 AAASIEAAEAAADN0AAE 13172 7782 10 AAASIEAAEAAADN0AAF 13172 7788 20 AAASIEAAEAAADN0AAG 13172 7839 10 AAASIEAAEAAADN0AAH 13172 7844 30 AAASIEAAEAAADN0AAI 13172 7876 20 AAASIEAAEAAADN0AAJ 13172 7902 20 AAASIEAAEAAADN0AAK 13172 7934 10 AAASIEAAEAAADN1AAA 13173 7499 40 AAASIEAAEAAADN1AAB 13173 7521 40 AAASIEAAEAAADN1AAC 13173 7900 40 tony@ORA11GR2> show parameter block_size NAME TYPE VALUE ------------------------------------ ---------------------- ------------------------------ db_block_size integer 8192
可以看到:
1)由于我们设置了聚簇的SIZE参数为2048,块大小为8K,所以1个块上最多放3个唯一的聚簇键。因此DEPNO为40的数据放到了另外1个块上。
2)DEPARTMENT和EMPLOYEE中的ROWID有重复。说明ROWID只是在表中唯一。
什么时候适合是用索引聚簇表:
· 数据主要用于读(这并不表示“从来不写”;聚簇表完全可以修改),而且要通过索引来读(可以是聚簇键索引,也可以是聚簇表上的其他索引)
· 需要频繁地把逻辑上相关而且总是一起使用的表信息联结在一起
Oracle数据字典就大量使用了聚簇表。例如
select cluster_name, table_name from dba_tables where cluster_name='C_OBJ#';
C_OBJ#聚簇中存储的主要是与列相关的信息,所以关于表或索引列集的所有信息都物理地存储在同一个块上。Oracle解析一个查询时,它希望访问所引用的表中所有列的数据。如果这些数据都在一个块上,通常就能很容易地得到。
什么时候不适合使用聚簇表:
· 如果预料到聚簇中的表会大量修改。索引聚簇会对DML的性能产生某种负面影响。
· 如果需要对聚簇中的某个表执行全表扫描。全表扫描不只是读取了需要的某个表中的数据,还读取了其它的多个表中的数据,全表扫描耗时更久。
· 如果需要频繁地TRUNCATE和加载表。聚簇中的表不能截除,因为聚簇在一个块上存储了多个表,必须删除聚簇表中的行。
· 如果需要对表进行分区。聚簇中的表不能被分区。聚簇本身也不能被分区。