再上一篇:14.2并行查询
上一篇:14.3并行DML
主页
下一篇:14.5并行恢复
再下一篇:14.6过程并行化
文章列表

14.4并行DDL

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

我认为,Oracle并行就是中真正的“闪光点“就是并行DDL。我们讨论过,并行执行通常不适用于 OLTP系统。实际上,对于许多数据仓库来说,并行查询也变得越来越没有意义。过去,数据仓库往往是为 一个很小、很集中的用户群建立的,有时只包括一两个分析人员。不过,在过去的10年中,我发现,数据 仓库的用户群已经从很小的规模发展为包括数百甚至上千位的用户。考虑这样一个数据仓库,它以一个基 于Web的应用作为前端,按理讲,数千(甚至更多)的用户都可以即时地访问这个数据仓库。
但是如果是DBA执行大规模的批操作(可能在一个维护窗口中执行)则是另一码事。DBA只是一个人
(而不是很多用户),他可能有一台能力很强的机器,有丰富的计算资源可用。DBA只有“一件事要做“: 加载这个数据,重组表,再重建索引。如果没有并行执行,DBA将很难真正充分利用硬件的全部能力。但 如果利用并行执行,则完全可以做到。以下SQL DDL命令允许”并行化“:
CREATE INDEX:多个并行执行服务器可以扫描表、对数据排序,并把有序的段写出到索引 结构。
CREATE TABLE AS SELECT:执行SELECT的查询可以使用并行查询来执行,表加载本身可以 并行完成。
ALTER INDEX REBUILD:索引结构可以并行重建。 ALTER TABLE MOVE:表可以并行移动。
ALTER TABLE SPLIT|COALESCE PARTITION:单个表分区可以并行地分解或合并。 ALTER INDEX SPLIT PARTITION:索引分区可以并行地分解。
前4个命令还适用于单个的表/索引分区,也就是说,可以并行地MOVE一个表的单个分区。
对我来说,并行DDL才是Oracle中并行执行最突出的优点。当然,并行查询可以用来加快某些长时 间运行的操作,但是从维护的观点看,以及从管理的角度来说,并行DDL才是影响我们(DBA和开发人员) 的并行操作。如果认为并行查询主要是为最终用户设计的,那么并行DDL则是为DBA/开发人员设计的。

14.4.1并行 DDL和使用外部表的数据加载

Oracle9i中我最喜欢的新特性之一是外部表(external table),在数据加载方面外部表尤其有用。 我们将在下一章详细地介绍数据加载和外部表。不过,作为一个简要说明,现在先简单地了解一下有关内 容,以便讨论并行DDL对于确定区段大小和区段截断(extent trimming)有何影响。
利用外部表,我们可以很容易地执行并行直接路径加载,而无需太多的考虑。Oracle 7.1允许我们 执行并行直接路径加载,即多个会话可以直接写至Oracle数据文件,而完全绕过缓冲区缓存,避开表数据 的 undo生成,可能还可以避免redo生成。这是通过 SQL*Loader 完成的。DBA 必须编写多个 SQL*Loader
会话的脚本,手动地分解要加载的输入数据文件,确定并行度,并协调所有SQL*Loader进程。简单地说, 尽管这是可以做到的,但很困难。
利用并行DDL,再加上外部表,就能通过一个简单的 CREATE TABLE AS SELECT or INSERT /*+ APPEND
*/来实现并行直接路径加载。不用再编写脚本,不必再分解文件,也不用协调要运行的N 个脚本。简单地 说,通过结合并行DDL和外部表,不仅提供了纯粹的易用性,而且全无性能损失。
下面来看这方面的一个简单的例子。稍后将看到如果创建一个外部表。下一章还会更详细地介绍如果 利用外部表实现数据加载。不过对现在来说,我们只是使用一个“真实“表(而不是外部表),由此加载另 一个表,这类似于许多人在数据仓库中使用暂存表(staging table)加载数据的做法。简单地说,这些技 术如下:
(1) 使用某个抽取、转换、加载(extract, transform, load, ETL)工具来创建输入文件。
(2) 将执行输入文件加载到暂存表。
(3) 使用对这些暂存表的查询加载一个新表。
还是使用前面的BIG_TABLE(启用了并行),其中包含10,000,000行记录。我们将把这个表联结到第 二个表USER_INFO,其中包含ALL_USERS字典视图中与OWNER相关的信息。我们的目标是将这个信息逆规 范化为一个平面结构。
首先创建USER_INFO表,启用并行操作,然后生成这个表的统计信息:
big_table@ORA10GR1> create table user_info as select * from all_users;
Table created.
big_table@ORA10GR1> alter table user_info parallel; Table altered.
big_table@ORA10GR1> exec dbms_stats.gather_table_stats( user, 'USER_INFO' ); PL/SQL procedure successfully completed.

现在,我们想用这个信息采用并行直接路径加载来加载一个新表。这里使用的查询很简单:
create table new_table parallel
as

select a.*, b.user_id, b.created user_created from big_table a, user_info b


where a.owner = b.username
在Oracle 10g中,这个特定CREATE TABLE AS SELECT的计划如下所示:
---------------------------------------------------------------------------------------
-----------------------------------------
| Id | Operation | Name | TQ
|IN-OUT | PQ Distrib |
---------------------------------------------------------------------------------------
-----------------------------------------
| 0 | CREATE TABLE STATEMENT | | |
| |

| 1

|

PX COORDINATOR

|

|

|

|

|

| 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC
(RAND) |
| 3 | LOAD AS SELECT | | Q1,01 | PCWP
| |
|* 4 | HASH JOIN | | Q1,01
| PCWP | |
| 5 | PX RECEIVE | | Q1,01 | PCWP | |
| 6 | PX SEND BROADCAST | :TQ10000 | Q1,00 | P->P | BROADCAST
|
| 7 | PX BLOCK ITERATOR | | Q1,00 | PCWC |
|
| 8 | TABLE ACCESS FULL | USER_INFO | Q1,00 | PCWP |
|
| 9 | PX BLOCK ITERATOR | | Q1,01 | PCWC |
|
| 10 | TABLE ACCESS FULL | BIG_TABLE | Q1,01 | PCWP |
|
---------------------------------------------------------------------------------------

-----------------------------------------
如果从第4步向下看,这些就是查询(SELECT)部分。BIG_TABLE的扫描和与USER_INFO的散列联结
是并行执行的,并将各个子结果加载到表的某个部分中(第3步,LOAD AS SELECT)。每个并行执行服务器 完成其联结和加载工作后,会把其结果发送给查询协调器。在这里,结果只是提示“成功“还是”失败“, 因为工作已经执行。
仅此而已,利用并行直接路径加载,使得问题变得很容易。对于这些操作,最重要的是要考虑如何使 用空间(或不使用)。有一种称为区段截断(extent trimming)的副作用相当重要,现在我要花一些时间 来讨论这个内容。

14.4.2并行 DDL和区段截断

并行DDL依赖于直接路径操作。也就是说,数据不传递到缓冲区缓存以便以后写出;而是由一个操作
(如CREATE TABLE AS SELECT)来创建新的区段,并直接写入这些区段,数据直接从查询写到磁盘(放在 这些新分配的区段中)。每个并行执行服务器执行自己的部分CREATE TABLE AS SELECT工作,并且都会写 至自己的区段。INSERT /*+ APPEND */(直接路径插入)会在一个段的HWM“之上“写,每个并行执行服 务器再写至其自己的一组区段,而不会与其他并行执行服务器共享。因此,如果执行一个并行CREATE TABLE AS SELECT,并使用4个并行执行服务器来创建表,就至少有4 个分区,可能还会更多。每个并行执行服务 器会分配其自己的区段,向其写入,等填满时,再分配另一个新的区段,并行执行服务器不会使用由其他 并行执行服务器非品牌的区段。
图14-3显示了这个过程。这里由4个并行执行服务器执行一个CREATE TABLE NEW_TABLE AS SELECT。 在图中,每个并行执行服务器分别用一种不同的颜色表示(白色、浅灰色、深灰色或黑色)。“磁鼓“中的 方框表示这个CREATE TABLE语句在某个数据文件中创建的区段。每个区段分别用上述某个颜色表示,之所 以能这样显示,原因很简单,任何给定区段中的所有数据都只会由这4 个并行执行服务器中之一加载,在 此显示出,P003创建并加载了其中4个区段。另一方面,P000则创建并加载了5个区段,等等。

图14-3 并行DDL区段分配示意图
初看上去一切都很好。但是在数据仓库环境中,执行一个大规模的加载之后,这可能导致“过渡浪费 “。假设你想加载1,010MB的数据(大约1GB),而且正在使用一个有100MB区段的表空间,你决定使用10 个并行执行服务器来加载这个数据。每个并行执行服务器先分配其自己的100MB区段(总共会有 10个100MB 的区段),并在其中填入数据。由于每个并行执行服务器都要加载101MB 的数据,所以它会填满第一个区段 , 然后再继续分配另一个100MB 的区段,但实际上只会使用这个区段中1MB的空间。现在就有了 20区段,其 中10个是满的,另外10个则不同,这10个区段中都各有1MB的数据,因此,总共会有990MB的空间是” 已分配但未使用的“。下一次加载是可以使用这个空间,但是对现在来说,你就有了990MB的死空间。此 时区段截断(extend trimming)就能派上用场了。Oracle会试图取每个并行执行服务器的最后一个区段, 并将其”截断为“可能的最小大小。

1. 区段截断和字典管理表空间

如果使用传统的字典管理表空间,Oracle可以把只包含1MB数据的各个100MB区段转变或1MB的区 段。遗憾的是,(在字典管理的表空间中)这会留下10个不连续的99MB空闲区段,因为你的分配机制采用 的是100MB 区段,所以这990MB空间就会用不上!下一次分配100MB 时,往往无法使用现有的这些空间, 因为现在的情况是:有99MB的空闲空间,接下来是1MB的已分配空间,然后又是99MB空闲空间,依此类 推。在本书中,我们不再复习字典管理方法。

2. 区段截断和本地管理表空间

现在来看本地管理表空间。对此有两种类型:UNIFORM SIZE 和AUTOALLOCATE,UNIFORM SIZE是指表 空间中的每个区段大小总是完全相同;AUTOALLOCATE则表示Oracle会使用一种内部算法来确定每个区段 应该是多大。这些方法都能很好地解决上述问题(即先有99MB空闲空间,其后是1MB已用空间,再后面又
是99MB空闲空间,依此类推,以至于存在大量无法使用的空闲空间)。不过,这两种方法的解决策略截然 不同。
UNIFORM SIZE方法完全排除了区段截断。如果使用UNIFORM SIZE,Oracle就不能执行区段截断。所 有区段都只能有惟一一种大小,不能有任何区段小于(或大于)这个大小。
另一方面,AUTOALLOCATE区段支持区段截断,但是采用了一种聪明的方式。AUTOALLOCATE方法使用 一些特定大小的区段,而且能使用不同大小的空间。也就是说,利用这种算法,一段时间后将允许使用表 空间中的所有空闲空间。在字典管理的表空间中,如果请求一个100MB 区段,倘若Oracle 只找到了99MB 的自由区段,请求还是会失败(尽管离要求只差了一点点)。与字典管理表空间不同,有AUTOALLOCATE区 段的本地管理表空间可以更为灵活。为了试图使用所有空闲空间,它可以减小所请求的空间大小。
下面来看这种本地管理表空间方法之间的差别。为此,我们需要处理一个实际的例子。这里将建立一 个外部表,它能用于并行直接路径加载(我们会频繁地执行并行直接路径加载)。即使你还在使用 SQL*Loader 来实现并行直接路径加载数据,这一节的内容也完全适用,只不过要求你自己编写脚本来执行数据的具体 加载。因此,为了讨论区段截断,我们需要建立加载示例,然后在不同的条件下执行加载,并分析结果。
环境建立
首先需要一个外部表。我多次发现,加载数据时我常常用到 SQL*Loader的一个遗留控制文件。例如, 可能如下所示:
LOAD DATA
INFILE '/tmp/big_table.dat' INTO TABLE big_table
REPLACE
FIELDS TERMINATED BY '|' (
id ,owner ,object_name ,subobject_name ,object_id
,data_object_id ,object_type ,created ,last_ddl_time
,timestamp ,status ,temporary ,generated ,secondary
)


使用SQL*Loader就可以很容易地把这个文件转换为一个外部表定义:
$ sqlldr big_table/big_table big_table.ctl external_table=generate_only
SQL*Loader: Release 10.1.0.3.0 - Production on Mon Jul 11 14:16:20 2005
Copyright (c) 1982, 2004, Oracle. All rights reserved.

注意传递给SQL*Loader的参数EXTERNAL_TABLE。在这里,这个参数使得SQL*Loader不会加载数据, 而是在日志文件中为我们生成一个CREATE TABLE 语句。这个 CREATE TABLE 语句如下所示(在此有删减; 为了让这个例子更简短,我对一些重复的元素进行了编辑):
CREATE TABLE "SYS_SQLLDR_X_EXT_BIG_TABLE"
(
"ID" NUMBER,
...
"SECONDARY" VARCHAR2(1)
)
ORGANIZATION external
(
TYPE oracle_loader
DEFAULT DIRECTORY SYS_SQLLDR_XT_TMPDIR_00000
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE CHARACTERSET WE8ISO8859P1
BADFILE 'SYS_SQLLDR_XT_TMPDIR_00000':'big_table.bad' LOGFILE 'big_table.log_xt'
READSIZE 1048576
FIELDS TERMINATED BY "|" LDRTRIM REJECT ROWS WITH ALL NULL FIELDS (
"ID" CHAR(255) TERMINATED BY "|",
....

"SECONDARY" CHAR(255)
TERMINATED BY "|"
)
)
location
(
'big_table.dat'
)
)REJECT LIMIT UNLIMITED
我们所要做的只是给这个外部表起一个我们想要的名字:可能要改变目录等:
ops$tkyte@ORA10GR1> create or replace directory my_dir as '/tmp/'
2 /
Directory created.


在此之后,只需具体创建这个表:
ops$tkyte@ORA10GR1> CREATE TABLE "BIG_TABLE_ET"
2 (
3 "ID" NUMBER,
...
16 "SECONDARY" VARCHAR2(1)
17 )
18 ORGANIZATION external
19 (
20 TYPE oracle_loader
21 DEFAULT DIRECTORY MY_DIR

22 ACCESS PARAMETERS
23 (
24 RECORDS DELIMITED BY NEWLINE CHARACTERSET WE8ISO8859P1
25 READSIZE 1048576
26 FIELDS TERMINATED BY "|" LDRTRIM
27 REJECT ROWS WITH ALL NULL FIELDS
28 )
29 location
30 (
31 'big_table.dat'
32 )
33 )REJECT LIMIT UNLIMITED
34 /
Table created.
然后使这个表启用并行(PARALLEL)。这是很神奇的一步,正是这一步使我们可以很容易地执行并行
直接路径加载:
ops$tkyte@ORA10GR1> alter table big_table_et PARALLEL;
Table altered.

注意 PARALLEL子句也可以在 CREATE TABLE 语句本身之上使用。也就是说,可以在 REJECT LIMIT
UNLIMITED后面增加关键字 PARALLEL。我之所以使用 ALTER 语句,只是想让读者更注意这一点: 外部表确实启用了并行。
使用UNIFORM与AUTOALLOCATE本地管理表空间的区段截断 要建立加载组件,所要在的工作就这么多。现在,我们想分析在使用 UNIFORM区段大小的本地管理表
空间(locally-managed tablespace,LMT)中如果管理空间,以及在使用AUTOALLOCATE区段的LMT中如

果管理空间,并对二者做一个比较。在这个例子中,我们将使用100MB 的区段。首先创建 LMT_UNIFORM, 它使用统一的区段大小:
ops$tkyte@ORA10GR1> create tablespace lmt_uniform
2 datafile '/u03/ora10gr1/lmt_uniform.dbf' size 1048640K reuse

3 autoextend on next 100m
4 extent management local
5 uniform size 100m; Tablespace created.
接下来,创建LMT_AUTO,它使用AUTOALLOCATE来确定区段大小:
ops$tkyte@ORA10GR1> create tablespace lmt_auto
2 datafile '/u03/ora10gr1/lmt_auto.dbf' size 1048640K reuse
3 autoextend on next 100m
4 extent management local
5 autoallocate; Tablespace created.

每个表空间开始时有一个1BG的数据文件(另外LMT还要用额外的64KB来管理存储空间:如果使用
32KB块大小,则另外需要128KB而不是64KB来管理存储空间)。我们允许这些数据文件一次自动扩展100MB。 下面要加载这个文件:
$ ls -lag big_table.dat
-rw-rw-r-- 1 tkyte 1067107251 Jul 11 13:46 big_table.dat

这是一个有10,000,000条记录的文件。它使用big_table.sql脚本创建(这个脚本见本书开头的“环
境配置“一节),再使用 flat.sql 脚本卸载(这个脚本可以从 http://asktom.oracle.com/~tkyte

/flat/index.html 得到)。接下来,执行一个并行直接路径加载,将这个文件加载到各个表空间中:
ops$tkyte@ORA10GR1> create table uniform_test
2 parallel
3 tablespace lmt_uniform
4 as
5 select * from big_table_et; Table created.
ops$tkyte@ORA10GR1> create table autoallocate_test

2 parallel
3 tablespace lmt_auto
4 as
5 select * from big_table_et; Table created.
在我的系统上(有4个CPU),使用了 8个并行执行服务器和一个协调器来执行这些CREATE TABLE语
句。运行这些语句时,可以通过查询与并行执行有关的一个动态性能视图V$PX_SESSION来验证这一点:
sys@ORA10GR1> select sid, serial#, qcsid, qcserial#, degree
2 from v$px_session;
SID SERIAL# QCSID QCSERIAL# DEGREE
---------- ---------- ---------- ---------- ----------
137 17 154 998 8
139 13 154 998 8
141 17 154 998 8
150 945 154 998 8
161 836 154 998 8
138 8 154 998 8
147 15 154 998 8
143 41 154 998 8
154 998 154
9 rows selected.

注意 创建UNIFORM_TEST和AUTOALLOCATE_SET表时,我们只是在每个表上指定了“并行“(parallel),
而由Oracle选择并行度。在这个例子中,我是这台机器的惟一用户(所有资源都可用),所以根 据CPU数(4)和PARALLEL_THREADS_PER_CPU参数设置(默认为2),Oracle会将并行度默认为8。
SID、SERIAL#是并行执行会话的标识符,QCSID、QCSERIAL#是并行执行查询协调器的标识符。因此,

如果运行着8个并行执行会话,我们可能想看看空间是如果使用的。通过对USER_SEGMENTS的一个快速查 询,就能清楚地知道:
ops$tkyte@ORA10GR1> select segment_name, blocks, extents
2 from user_segments
3 where segment_name in ( 'UNIFORM_TEST', 'AUTOALLOCATE_TEST' );
SEGMENT_NAME BLOCKS EXTENTS
--------------- ---------- ---------- UNIFORM_TEST 204800 16
AUTOALLOCATE_TEST 145592 714
由于我们使用的块大小是 8KB,这说明二者相差大约 462MB,或者从比例来看,AUTOALLOCATE_TEST

的已分配空间大约是UNIFORM_TEST的70%。如果查看实际使用的空间:
ops$tkyte@ORA10GR1> exec show_space('UNIFORM_TEST' );
Free Blocks............................. 59,224
Total Blocks............................ 204,800
Total Bytes............................. 1,677,721,600
Total MBytes............................ 1,600
Unused Blocks........................... 0
Unused Bytes............................ 0
Last Used Ext FileId.................... 6
Last Used Ext BlockId................... 9
Last Used Block......................... 12,800
PL/SQL procedure successfully completed.
ops$tkyte@ORA10GR1> exec show_space('AUTOALLOCATE_TEST' );

Free Blocks............................. 16
Total Blocks............................ 145,592
Total Bytes............................. 1,192,689,664
Total MBytes............................ 1,137
Unused Blocks........................... 0
Unused Bytes............................ 0
Last Used Ext FileId.................... 8
Last Used Ext BlockId................... 41
Last Used Block......................... 8
PL/SQL procedure successfully completed.
注意 SHOW_SPACE过程在本身开头的“环境配置“中已经介绍过。

可以看到,如果去掉UNIFORM_TEST中freelist所占的块数(59,224个空闲块),这两个表实际所占 的空间几乎是一样的,不过,UNIFORM表空间总共需要的空间确实大多了。这完全归因于没有区段截断。 如果查看UNIFORM_TEST,可以很清楚地看出:
ops$tkyte@ORA10GR1> select segment_name, extent_id, blocks
2 from user_extents where segment_name = 'UNIFORM_TEST';
SEGMENT_NAME EXTENT_ID BLOCKS
--------------- ---------- ---------- UNIFORM_TEST 0 12800
UNIFORM_TEST 1 12800
UNIFORM_TEST 2 12800
UNIFORM_TEST 3 12800
UNIFORM_TEST 4 12800
UNIFORM_TEST 5 12800

UNIFORM_TEST 6 12800
UNIFORM_TEST 7 12800
UNIFORM_TEST 8 12800
UNIFORM_TEST 9 12800
UNIFORM_TEST 10 12800
UNIFORM_TEST 11 12800
UNIFORM_TEST 12 12800
UNIFORM_TEST 13 12800
UNIFORM_TEST 14 12800
UNIFORM_TEST 15 12800
16 rows selected.
每个区段的大小都是100MB。再来看AUTOALLOCATE_TEST,如果把所有714个区段都列出来就太浪费

篇幅了,所以下面汇总起来看:
ops$tkyte@ORA10GR1> select segment_name, blocks, count(*)
2 from user_extents
3 where segment_name = 'AUTOALLOCATE_TEST'
4 group by segment_name, blocks
5 /
SEGMENT_NAME BLOCKS COUNT(*)
----------------- ---------- ---------- AUTOALLOCATE_TEST 8 128
AUTOALLOCATE_TEST 128 504
AUTOALLOCATE_TEST 240 1
AUTOALLOCATE_TEST 392 1

AUTOALLOCATE_TEST 512 1
AUTOALLOCATE_TEST 656 1
AUTOALLOCATE_TEST 752 5
AUTOALLOCATE_TEST 768 1
AUTOALLOCATE_TEST 1024 72
9 rows selected.
对于使用AUTOALLOCATE的LMT,通常都是这样来分配空间。这里的 8块、128块和1,024块的区段是
“正常“区段:使用AUTOALLOCATE时,总能观察到这样一些区段。不过,余下的区段就不那么”正常“了 , 观察时不一定能看到。这是因为发生了区段截断。有些并行执行服务器完成了它们的那部分加载后,取其 最后的8MB(1,024块)区段,并对其截断,这就留下了一些多余的空间。这样,如果另外某个并行执行会 话需要空间,就可以使用这部分多余的空间。陆续地,当另外这些并行执行会话处理完自己的加载时,也 会截断其最后一个区段,而留下一些多余的空间。
应该使用哪种方法呢?如果你的目标是尽可能经常并行地执行直接路径加载,我建议你采用 AUTOALLOCATE作为区段管理策略。诸如此类的并行直接路径操作不会使用对象 HWM以下的已用空间,即不 会用freelits上的空间。除非你还要对表执行一些传统路径插入,否则UNIFORM分配方法会在这些表中永 久地保留一些从不使用的额外空闲空间。除非你能把UNIFORM LMT的区段大小定得更小,否则,过一段时 间后,就会看到我所说的过度浪费的情况:而且要记住,这些空间与段有关,所以对表进行完全扫描时也 必须扫描这些空间。

为了说明这一点,下面使用同样的输入再对这两个表执行另一个并行直接路径加载:
ops$tkyte@ORA10GR1> alter session enable parallel dml;
Session altered.
ops$tkyte@ORA10GR1> insert /*+ append */ into UNIFORM_TEST
2 select * from big_table_et;
10000000 rows created.
ops$tkyte@ORA10GR1> insert /*+ append */ into AUTOALLOCATE_TEST
2 select * from big_table_et;
10000000 rows created.
ops$tkyte@ORA10GR1> commit;
Commit complete.


在这个操作之后,比较两个表的空间利用情况,如下:
ops$tkyte@ORA10GR1> exec show_space( 'UNIFORM_TEST' );
Free Blocks............................. 118,463
Total Blocks............................ 409,600
Total Bytes............................. 3,355,443,200
Total MBytes............................ 3,200
Unused Blocks........................... 0
Unused Bytes............................ 0
Last Used Ext FileId.................... 6
Last Used Ext BlockId................... 281,609
Last Used Block......................... 12,800
PL/SQL procedure successfully completed.
ops$tkyte@ORA10GR1> exec show_space( 'AUTOALLOCATE_TEST' );
Free Blocks............................. 48
Total Blocks............................ 291,184
Total Bytes............................. 2,385,379,328
Total MBytes............................ 2,274
Unused Blocks........................... 0
Unused Bytes............................ 0
Last Used Ext FileId.................... 8

Last Used Ext BlockId................... 140,025
Last Used Block......................... 8
PL/SQL procedure successfully completed.
可以看到,随着我们使用并行直接路径操作向表UNIFORM_TEST加载越来越多的数据,过一段时间后,
空间利用情况会变得越来越糟糕。对此,我们可能希望使用一个更小的统一区段大小,或者使用 AUTOALLOCATE。一段时间后,AUTOALLOCATE也可能生成更多的区段,但是由于会发生区段截断,所以空间 利用情况要好得多。