再上一篇:10.1表类型
上一篇:10.2术语
主页
下一篇:10.4索引组织表
再下一篇:10.5索引聚簇表
文章列表

10.3堆组织表

Oracle 9i 10g编程艺术:深入数据库体系结构

应 用中99%(或者更多)的情况下使用的可能都是堆组织表,不过随着IOT的出现,这种状况以后 可能会有所改观,因为IOT本身就可以加索引。执行 CREATE TABLE语句时,默认得到的表类型就是堆组 织表。如果你想要任何其他类型的表结构,就需要在CREATE语句本身中指定它。
堆 (heap)是计算机科学领域中得到深入研究的一种经典数据结构。它实际上就是一个很大的空间、 磁盘或内存区(当然,这里所说的磁盘是指数据库表的相应磁 盘),会以一种显然随机的方式管理。数据
会放在最合适的地方,而不是以某种特定顺序来放置。许多人希望能按数据放入表中的顺序从表中取出数 据,但是对于 堆,这是无法保证的。这一点很容易说清楚。
在以下的例子中,我将建立一个表,使得在我的数据库中每块刚好能放一个整行(我使用的块大小是
8KB)。不一定非得每块上有一行,我只是想利用这一点来展示一种可预测的事务序列。不论数据库使用多 大的块大小,也不论表的大小如何,都可以观察到以下行为(行没有次序):

ops$tkyte@ORA10GR1> create table t
2 ( a int,
3 b varchar2(4000) default rpad('*',4000,'*'),
4 c varchar2(3000) default rpad('*',3000,'*')
5 )
6 /
Table created.
ops$tkyte@ORA10GR1> insert into t (a) values ( 1);
1 row created.
ops$tkyte@ORA10GR1> insert into t (a) values ( 2);
1 row created.
ops$tkyte@ORA10GR1> insert into t (a) values ( 3);
1 row created.
ops$tkyte@ORA10GR1> delete from t where a = 2 ;
1 row deleted.
ops$tkyte@ORA10GR1> insert into t (a) values ( 4);
1 row created.
ops$tkyte@ORA10GR1> select a from t; A
----------

1
4
3
如 果你想再试试(得到同样的结果),可以根据你的块大小来调整B和C列。例如,如果你的块大 小为2KB,则不需要C 列,而且B列应该是一个VARCHAR2 (1500),默认有1,500个星号。在这样一个表中 , 由于数据在堆中管理,只要有空间变为可用,就会重用这个空间。
注意 使用ASSM或MSSM时,你会发现行最后会在“不同的位置上”。底层的空间管理例程有很大差别,在ASSM 和MSSM中,对同一个表执行同样的操作很可能得到不同的物理顺序。尽管数据逻辑是相同的,但是它们会 以不同的方式存储。
全 部扫描时,会按命中的顺序来获取数据,而不是以插入的顺序。这是一个必须了解的重要的数据 库表概念:一般来讲,数据库表本质上是无序的数据集合。还应该注 意到,要观察到这种效果,不必在 INSERT后接下来再使用DELETE;只需使用INSERT就可以得到同样的结果。如果我插入一个小行,那么观 察到的 结果很可能是:取出行时默认的顺序为“小行、小行、大行”。这些行并不按插入的顺序获取。Oracle 会把数据放在能放下的任何地方,而不是按照日期或事 务的某种顺序来存放。
如 果你的查询需要按插入的顺序来获取数据,就必须向表中增加一列,以便获取数据时使用这个列 对数据排序。例如,这可以是一个数字列,有一个递增的序列(使用 Oracle SEQUENCE对象)。只需使用 一个SELECT,其ORDER BY子句对这个列完成排序,这样就可以模拟插入顺序。这个顺序可能只是近似的, 因为序号为55的行很可能在序号为54的行之前提交,因此,数据库中序号为 55的行可能放在前面。
应该把堆组织表看作一个很大的无序行集合。这些行会以一种看来随机的顺序取出,而且取出的顺序 还取决于所用的其他选项(并行查询、不同的优化器模式,等待),同一个查询可能会以不同的顺序取出 数据。不要过分依赖查询得到的顺序,除非查询中有一个ORDER BY语句!
除 此之外,关于堆表还有什么重要的内容需要了解?要知道,Oracle SQL Reference手册中介绍 CREATE TABLE语法时足足用了72页,所以有关的选项当然多。由于存在如此之多的选项,所以很难全部 掌握。“线路图”(或“轨迹”图)本身就用了18页来介 绍。要了解一个给定表的CREATE TABLE语句中 主要有哪些可用的选项,我用了一个技巧。首先,尽可能简单地创建表,例如:

ops$tkyte@ORA10GR1> create table t
2 ( x int primary key,
3 y date,
4 z clob
5 )
6 /
Table created.
然后,使用标准内置包DBMS_METADATA,查询这个表的定义,并查看详细语法:

ops$tkyte@ORA10GR1> select dbms_metadata.get_ddl( 'TABLE', 'T' ) from dual;
DBMS_METADATA.GET_DDL('TABLE','T')
------------------------------------------------------------------------------- CREATE TABLE "OPS$TKYTE"."T"
( "X" NUMBER(*,0), "Y" DATE,

"Z" CLOB,
PRIMARY KEY ("X")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "USERS" ENABLE
) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "USERS"
LOB ("Z") STORE AS (
TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192 PCTVERSION 10
NOCACHE
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT))
这个技巧的好处是,它显示了CREATE TABLE语句的许多选项。我只需要提供数据类型,Oracle就会
为我生成详细的“版本”(CREATE TABLE版本)。现在我可以定制这个详细的版本,可能把 ENABLE STORAGE IN ROW改 成DISABLE STORAGE IN ROW,这样会禁用随结构化数据在行中存储LOB数据,而把LOB数据存 储在另外一个段中。我一直都在使用这个技巧来节省我的时间,因为要从那个庞大的线 路图中找出该使用 哪个选项很让人犯愁,如果不采用这个技巧,可能就会为此浪费好几分钟。使用这个技术还可以了解不同 的情况下CREATE TABLE语句有哪些可用的选项。
既然你知道了如何查看一个给定的CREATE TABLE语句可用的大多数选项,那么对于堆表来说,需要 注意哪些重要的选项呢?在我看来,对于ASSM有两个重要选项,对于MSSM,重要选项有4个:
FREELISTS: 仅适用于MSSM。每个表都会在一个freelist上管理堆中分配的块。一个表可以有多个 freelist。如果你认定会有多个并发用户对表执行大量的 插入,配置多个freelist可能会大大地改善性 能(可能要以额外的存储空间为代价)。这个设置对性能可能产生的影响请参见“FREELISTS”一节 中的 讨论和有关例子。
PCTFREE:ASSM和MSSM都适用。在INSERT过程中,会测量块的充满程度。如前所示,根据块当前充 满的程度,这个参数用于控制能否将一行增加到一个块上。这个选项还可以控制因后续更新所导致的行迁 移,要根据将如何使用表来适当地设置。
PCTUSED:仅适用于MSSM。度量一个块必须为多空才允许再次插入行。如果块中已用的空间小于 PCTUSED,就可以插入新行了。同样地,类似于 PCTFREE,必须考虑你将如何使用表,从而适当地设置这个 选项。
INITRANS:ASSM 和MSSM都适合。为块初始分配的事务槽数。如果这个选项设置得太低(默认值为2, 这也是最小值),可能导致多个用户访问的一个块上出现并发问题。如果一 个数据块机会已满,而且事务 表无法动态扩展,会话就会排队等待这个块,因为每个并发事务都需要一个事务槽。如果你认为会对同样 的块完成多个并发更新,就应 该考虑增大这个值。
注意 单独存储在LOB段中的LOB数据并不使用表的PCTFREE/PCTUSED参数设置。这些LOB块以不同的方式管 理:它们总是会填入,直至达到最大容量,而且仅当完全为空时才返回freelist。
这些参数要特别注意。随着本地管理表空间的引入(这也是强烈推荐的做法),我发现其余的参数(如 PCTINCREASE、NEXT等)已经没有什么意义了。