首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

Oracle 聚簇(征集)

2012-07-02 
Oracle 聚簇(收集)1. 什么是聚簇聚簇是根据码值找到数据的物理存储位置,从而达到快速检索数据的目的。Oracl

Oracle 聚簇(收集)

1. 什么是聚簇

聚簇是根据码值找到数据的物理存储位置,从而达到快速检索数据的目的。Oracle聚簇索引的顺序就是数据的物理存储顺序,叶节点就是数据节点。非聚簇索引的顺序与数据物理排列顺序无关,叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。一个表最多只能有一个聚簇索引。

2. 使用 Oracle聚簇索引

聚簇是一种存储表的方法,这些表密切相关并经常一起连接进磁盘的同一区域。例如,表 BOOKSHELF 和BOOKSHELF_AUTHOR 数据行可以一起插入到称为簇(Cluster)的单个区域中,而不是将两个表放在磁盘上的不同扇区上。簇键(Cluster Key)可以是一列或多列,通过这些列可以将这些表在查询中连接起来(例如,BOOKSHELF表和BOOKSHELF_AUTHOR表中的 Title列)。为了将表聚集在一起,必须拥有这些将要聚集在一起的表。

下面是create cluster命令的基本格式:

create cluster (column datatype [, column datatype]...) [other options];

cluster的名字遵循表命名约定,column datatype是将作为簇键使用的名字和数据类型。column的名字可以与将要放进该簇中的表的一个列名相同,或者为其他有效名字。下面是一个例子:

create cluster BOOKandAUTHOR (Col1 VARCHAR2(100));

这样就建立了一个没有任何内容的簇(象给表分配了一块空间一样)。COL1的使用对于簇键是不相干的,不会再使用它。但是,它的定义应该与要增加的表的主键相符。接下来,建立包含在该簇中的表:
create table BOOKSHELF
(Title VARCHAR2(100) primary key,
Publisher VARCHAR2(20),
CategoryName VARCHAR2(20),
Rating VARCHAR2(2),
constraint CATFK foreign key (CategoryName) references CATEGORY(CategoryName)
)
cluster BOOKandAUTHOR(Title);

在向BOOKSHELF表中插入数据行之前,必须建立一个Oracle聚簇索引:

create index BOOKandAUTHORndx on cluster BOOKandAUTHOR;

在上面的create table语句中,簇BOOKandAUTHOR(Title)子句放在表的列清单的闭括号的后面。BOOKandAUTHOR是前面建立的聚簇的名字。

Title是将存储到聚簇Col1中的该表的列。create cluster语句中可能会有多个簇键,并且在created table语句中可能有多个列存储在这些键中。请注意,没有任何语句明确说明Title列进入到Col1中。这种匹配仅仅是通过位置做到的,即Col1和Title都是在它们各自的簇语句中提到的第一个对象。多个列和簇键是第一个与第一个匹配,第二个与第二个匹配,第三个与第三个匹配,等等。现在,添加第二个表到聚簇中:
create table BOOKSHELF_AUTHOR
(Title VARCHAR2(100),
AuthorName VARCHAR2(50),
constraint TitleFK Foreign key (Title) references BOOKSHELF(Title),
constraint AuthorNameFK Foreign key (AuthorName) references AUTHOR(AuthorName)
)
cluster BOOKandAUTHOR (Title);

当这两个表被聚在一起时,每个唯一的Title在簇中实际只存储一次。对于每个Title,都从这两个表中附加列。

来自这两个表的数据实际上存放在一个位置上,就好像簇是一个包含两个表中的所有数据的大表一样。

3. 散列聚簇

对于散列聚簇,它只有一个表。它通过散列算法求出存储行的物理存储位置,从而快速检索数据。创建散列聚簇时要指定码列的数据类型,数据行的大小及不同码值的个数。如果码值不是平均分布的,就可能有许多行存储到溢出块上,从而会降低查询该表的SQL语句的性能。

散列聚簇被用在总是通过主键查询数据的情况,例如要从表 T 查询数据并且查询语句总是是这样:

select * from T where id = :x;

这时散列聚簇是一个好的选择,因为不需要索引。Oracle 将通过散列算法得到值 :x 所对应的物理地址,从而直接取到数据。不用进行索引扫描,只通过散列值进行一次表访问

?

索引聚簇表 适用范围

索引聚簇表
create cluster emp_dept_cluster
(deptno number(2)) size 1024;
size 1024 表示每个聚簇键值关联大约1024字节的数据,
oracle会在用这个数据库块上通过size计算最多可以放多少个簇
如果块是8KB,那么这个块上最多放7个聚簇键

?

向聚簇中放数据之前,需要先对聚簇建立索引.
create index emp_dept_cluster_idx on cluster emp_dept_cluster;

?

加载的方式应彩用一一对应的关系.加载完主表之后再加载从表

?

什么情况下不能用索引聚簇表
1)如果预料到聚簇中的表会大量修改,索引聚簇表会对DML的性能产生负面影响.
2)非常不适合对单表的全表扫描,因为只能引起对其它表的全表扫描
3)频繁对表进行TRUNCATE和加载,因为聚簇中的表是不能TRUNCATE的

?

SQL> truncate table dept;
truncate table dept
*
ERROR at line 1:
ORA-03292: Table to be truncated is part of a cluster

?

?

?

如果数据主要用来读,不怎么修改,并且逻辑上与聚簇连接想适合,最好使用索引聚簇表
oracle数据字典就是这样做的

?

SQL> set autotrace traceonly statistics
SQL> select a.deptno,b.ename from dept_02 a,emp_02 b where a.deptno=b.deptno and
a.deptno='30';

?

6 rows selected.

?

Execution Plan
----------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 MERGE JOIN
2 1 INDEX (UNIQUE SCAN) OF 'dddd' (UNIQUE)
3 1 FILTER
4 3 TABLE ACCESS (FULL) OF 'EMP_02'

?


Statistics
----------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
470 bytes sent via SQL*Net to client
495 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6 rows processed

?

SQL> select a.deptno,b.ename from dept a,emp b where a.deptno=b.deptno and a.dep
tno='30';

?

6 rows selected.

?


Execution Plan
----------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 INDEX (UNIQUE SCAN) OF 'SYS_C002891' (UNIQUE)
3 1 TABLE ACCESS (CLUSTER) OF 'EMP'

?


Statistics
----------------------
0 recursive calls
0 db block gets
4 consistent gets --这里可以看出有点优点,在consistent gets上少了一块
0 physical reads
0 redo size
470 bytes sent via SQL*Net to client
495 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6 rows processed

?

SQL> select a.deptno,b.ename from dept a,emp b where a.deptno=b.deptno;

?

14 rows selected.

?

Execution Plan
----------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'EMP'
3 1 TABLE ACCESS (CLUSTER) OF 'DEPT'

?


Statistics
----------------------
0 recursive calls
0 db block gets
37 consistent gets --全表扫描真是too bad,没有办法,这里不适用索引聚簇表
0 physical reads
0 redo size
581 bytes sent via SQL*Net to client
495 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed

?

SQL> select a.deptno,b.ename from dept_02 a,emp_02 b where a.deptno=b.deptno;

?

14 rows selected.

?

Execution Plan
----------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'EMP_02'
3 1 INDEX (UNIQUE SCAN) OF 'dddd' (UNIQUE)

?


Statistics
----------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
581 bytes sent via SQL*Net to client
495 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed

?

?

?(点击查看大图)图1-3

通过仔细观察图1-3可以发现,在表中,按照日期的顺序对数据行进行了存储。对表中的行进行详细分析可以发现,由于数据中的日期就是行被插入的日期,所以数据也就很自然地被存储在相似的位置上。还可以发现在连续的日期中间偶尔会出现相差比较远的日期,这主要是由于对行所执行的修改或删除操作所致。也正是因为修改和删除操作才使得在当前数据块中出现了空余空间,并当其大小超过预先设置的特定值(PCTUSED)时,就将该数据块记录到Free List中以待再次使用。

实际上,通常对存储在表中的行执行删除操作并不会频繁出现,并且修改操作导致列的长度变短的情况也比较少见。通常我们会尽量重复使用现存数据而尽量避免新增数据,因此,通常数据插入操作的情况同样也比我们想象的要少(这种现象虽然会随着表特征的不同而不同,但是通常都如此)。需要注意的是,在这里并不是指所有的情况,在特殊的应用环境中并不一定是这样。

在索引中是按照索引列和ROWID进行排序的。在索引创建时,既可以按照升序(Ascending)也可以按照降序(Descending)进行排序。不论是升序还是降序都与数据读取的效率有着密切的联系,因此,不论如何选择都要有其合理的理由,不能随心所欲,这部分内容将在本书的后面予以详细说明。

我们经过分析行的读取情况,不仅能够发现数据的读取每次都是以数据块为单位,而且能够发现位于同一数据块中的行并没有遵循特定的顺序。这些索引行实际上是按照索引列和ROWID来进行排序的。按照ROWID对索引行进行排序,实际上是按照物理数据文件的数据块号对数据块进行排序,然后再按照Slot号码对数据块中的行进行排序。

由此可见,就像图1-3所示的那样,由于按照数据块的号码对数据块进行了排序,所以我们能够看出DBMS在以块为单位执行数据读取时,为了在一个数据块中读取尽可能多的行所做的努力。尽管数据块中的行没有遵循特定的顺序,但利用Slot中所存储的行的位置信息也始终能够找到期望的行。

这也就意味着,在索引中按照ROWID进行排序只不过是在数据块中按照Slot号码进行了排序,这并不会对行的读取产生任何影响。在图1-3中,尽管能够按照进行排序了的Slot号码读取数据块中的行,但也正是由于这个原因导致行读取顺序不规则现象的出现。

现在让我们再来分析一下,使用位于图1-3左右两侧的索引来读取特定范围中行的情况。我们可以看到,把相同的数据集中存储在两个数据块中位于左侧的INDEX1,和把相同的数据分散存储在不同数据块中位于右侧的INDEX2。INDEX1虽然需要读取7行数据,但实际上却只读取了两个数据块;而INDEX2虽然只需要读取3行数据,但却读取了3个数据块。

我们知道数据的存储方式一旦被决定,不论在何种情况下都不能随意改变。就像图1-3所示的那样,尽管索引是在表的基础上创建的,但索引行的顺序仍然与表中行的位置在相似程度上存在着一定差异。因此,如果利用聚簇因子较好的索引读取数据,则即使所需要读取的行数较多,也能因为读取数据块数较少而能够获得非常好的读取效率。由此可见,在频繁需要读取大范围数据(大范围数据是指位于开始行与结束行之间的满足查询条件的连续数据行,在本书中将其称之为大范围数据)的情况下,为了提高读取效率而采用有利于数据读取的存储方式就会显得非常重要。

例如,要到达同一地点,则居住在交通便利但较远的地方,比居住在交通不便利但较近的地方要快。通常我们都会在交通要地修建高速公路、飞机场、高速铁路等快速便利的交通设施。也正是由于这些便利的交通设施,才使得我们现在的生活和出行变得轻松方便。同理,如果以较小的代价就能够有效地处理经常需要访问的大范围数据,则就可以获得非常好的效率。

如果表中行的存储顺序与我们经常读取较大范围行的顺序一致,则就能够在很大程度上提高读取效率。然而,这两种顺序的一致只不过是偶尔才会出现的现象,大部分情况并非如此。因此为了提高数据的读取效率,就需要我们针对不同的情况制定出不同的战略措施,以提高聚簇因子。按照经常频繁读取的大范围数据的读取顺序来存储数据、提高聚簇因子的措施中将会涉及多方面的因素。

事实上,提高聚簇因子的有效方法有很多,但这些方法的不足之处就在于在数据存储时需要付出的代价较大。在提高聚簇因子方面显得最无效的方法就是堆表,因为这种表结构所采用的并不是对数据的存储具有强制性约束的固定存储方式,而是按照数据插入的顺序进行存储的随机存储方式。

如果通过少量额外代价就可以按我们所期望的方式来提高聚簇因子,则也算是实现了以较小代价换取较高读取效率的目的。这里所谓的"期望的方式"是指按照特定的方法存储数据的方式。

即使我们所期望的数据存储顺序与数据生成的顺序不一致,也不应当为此而耿耿于怀。只需要定期对表执行重构操作就可以达到所期望的效果。然而,我们绝对不可以忽视表重构操作的代价,而且它的代价并不像我们想象的那么小。因此,表的重构操作也不是随心所欲的事情。

尽管这里强调了表重构操作的代价并不小,但这只是为了让各位读者引起重视,事实上,表重构操作的代价并不是很大。即使表中存储的是海量数据也不用担心,因为现在DBMS的功能不断提高,并且此项操作通常都是放在空闲时间来实施的,所以不会给系统造成太大的负担。另外,对表的重构操作并不一定要等到非常需要时才去执行。重构操作的周期可能会随着实际情况的不同而不同,但即使几个月执行一次也同样能够充分地实现所期望的目的。

即使此项操作由于其他原因而搁置了一段时间,但除了聚簇因子稍微有所变坏之外,其他部分并不会受到任何影响,而可以将此项操作继续推迟到有空闲时间时再去执行。如果为表创建了分区,则由于在此期间有可能只使用了一部分分区中的数据,就更加不会影响数据的读取效率了。对表的重构操作影响最大的因素是并行处理(Parallel Processing)。

对于究竟应当按照哪个列的顺序存储数据的综合性战略方案,将在第4章予以系统说明。从某种意义上来看,这里所谓的索引战略方案与其说是为了索引的构成形态而制定的战略方案,还不如说是通过对可能出现的所有读取类型的全面分析,制定出能够实现最优数据读取的最理想的索引战略方案。

鉴于此,在堆表结构中,应当从战略性的高度对将要存储的列的顺序,予以综合性的分析和判断。

热点排行