有时候在应用系统中,不正确的使用并行查询也会导致应用问题,以下是一个实际生产中的案例。Statspack 的 Top 5 时间事件输出显示 direct path read 消耗了较高的等待:
Top 5 Timed Events
~~~~~~~~~~~~~~~~~~ % Total
Event Waits Time (s) Ela Time
-------------------------------------------- ------------ ----------- --------
CPU time 5,035 39.49
db file sequential read 444,011 3,582 29.09
direct path read 124,451 1,351 10.60
db file scattered read 389,933 908 7.12
KJC: Wait for msg sends to complete 31,985 600 4.70
-------------------------------------------------------------
而这个数据库的内存排序率是 100%(In-memory Sort %: 100.00),显然这里的 Direct Path Read 并不是由于排序引发的,注意到另外一个等待事件(KJC: Wait for msg sends to complete)和并行有关,所以初步判断这里的 direct path read 可能和并行有关。
进一步检查 Statspack 报告中的 SQL 部分,发现大量并行查询改写出来的 SQL,这些 SQL通过内部ᨀ示(Hints)固化其执行路径:
250,458 133 1,883.1 11.9 3.30 29.80 883303536
Module: yy_glxt.exe
SELECT /*+ ORDERED NO_EXPAND USE_NL(A2) INDEX(A2 "SYS_C005617")
*/ A1.C0,A1.C1,A1.C2,A1.C3,A1.C4,A1.C5,A1.C6,A1.C7,A1.C8,A1.C9,A
1.C10,A1.C11,A1.C12,A1.C13,A1.C14,A1.C15,A1.C16,A1.C17,A1.C18,A1
.C19,A1.C20,A1.C21,A1.C22,A1.C23,A1.C24,A1.C25,A1.C26,A1.C27,A1.
C28,A1.C29,A1.C30,A1.C31,A1.C32,A1.C33,A1.C34,A1.C35,A1.C36,A1.C
201,912 9 22,434.7 9.6 3.82 239.60 1246116920
Module: dmxt.exe
SELECT /*+ Q84417000 NO_EXPAND ROWID(A1) */ A1."ZXBMDM" C0,A1."F
MDM" C1,A1."FMGG" C2,A1."PFJE" C3,A1."JE" C4,A1."LSL" C5,A1."FMM
C" C6 FROM "YYGL"."MZ101_2" PX_GRANULE(0, BLOCK_RANGE, DYNAMIC)
A1 WHERE (TO_CHAR(A1."SFRQ",'yyyy/mm/dd')=:B1 AND A1."JE">0 OR
TO_CHAR(A1."TFRQ",'yyyy/mm/dd')=:B2 AND A1."JE"<0) AND RTRIM(A1.
在很多情况下,并行也许并不是最好的选择,如果表并不大,并行反而会降低其执行速
度。这个用户环境正是如此,询问用户,从未主动启用并行。
通过查询 DBA_TABLES 字典表可以获得 Degree 并行度的记录,并行度大于 1 的数据表
在查询时会启用并行,但是注意事实还会有所不同, Degree 字段的类型及长度是
VARCHAR2(10)。所以注意,当使用类似如下查询时,可能无法获得返回值:
SQL> select table_name from dba_tables where degree='1' or degree='DEFAULT';
no rows selected
我们看一下 Degree 以及 Instances 的记录方式:
SQL> select degree,length(degree) from dba_tables group by degree;
DEGREE LENGTH(DEGREE)
-------------------- --------------
DEFAULT 10
1 10
SQL>select instances,length(instances) from dba_tables group by instances;
INSTANCES LENGTH(INSTANCES)
-------------------- -----------------
DEFAULT 10
1 10
0 10
Degree 和 Instances 实际上记录了 10 个字符,左端用空格补齐。在 dba_tables 的创建语句中,可以找到根本原因,以下是这两个字段的定义来源:
lpad(decode(t.degree, 32767, 'DEFAULT', nvl(t.degree,1)),10),
lpad(decode(t.instances, 32767, 'DEFAULT', nvl(t.instances,1)),10),
而需要注意的是,如果 Degree 设置为 DEFAULT,则默认数据库会对该表启用并行。最
后找到相关的 SQL,从 AUTOTRACE 可以看到这些 SQL 的执行计划:
SQL> SELECT t1.fmdm, t1.fmmc, t1.sfdldm, t2.sfdlmc, t1.sfxldm, t3.sfxlmc, t1.fmdm,
2 t1.fmmc, t1.dw, t1.dj, t1.fmshrm, NVL (t1.jeflag, '0'), t1.htbh,
3 NVL (t1.zhflag, 0), t1.ybfl, t1.ybdm, '3'
4 FROM sf007 t1, sf001 t2, sf006 t3
5 WHERE t2.mzflag = '1'
6 AND t1.sfdldm = t2.sfdldm
7 AND t1.sfdldm = t3.sfdldm(+)
8 AND t1.sfxldm = t3.sfxldm(+)
9 /
1005 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=16 Bytes=2192)
1 0 HASH JOIN* (Cost=8 Card=16 Bytes=2192):Q91507003
2 1 TABLE ACCESS* (FULL) OF 'SF001' (Cost=2 Card=4 Bytes=52):Q91507000
3 1 HASH JOIN* (OUTER) (Cost=6 Card=1634 Bytes=202616):Q91507003
4 3 TABLE ACCESS* (FULL) OF 'SF007' (Cost=5 Card=1634 Bytes=160132):Q91507001
5 3 TABLE ACCESS* (FULL) OF 'SF006' (Cost=1 Card=409 Bytes=10634):Q91507002
1 PARALLEL_TO_SERIAL SELECT /*+ ORDERED NO_EXPAND USE_HASH(A2) SW
AP_JOIN_INPUTS(A2) */ A1.C0,A2.C1,A1
2 PARALLEL_FROM_SERIAL
3 PARALLEL_COMBINED_WITH_PARENT
4 PARALLEL_FROM_SERIAL
5 PARALLEL_TO_PARALLEL SELECT /*+ NO_EXPAND ROWID(A1) */ A1."SFDLDM
" C0,A1."SFXLDM" C1,A1."SFXLMC" C2 F
查看涉及数据表的并行度,注意到其并行度被设置为 DEFAULT:
SQL> select table_name,degree from dba_tables where table_name=’SF006’;
TABLE_NAME DEGREE
------------------------------ ----------
SF006 DEFAULT
将表的并行度修改为 1 后,问题得以解决:
SQL> alter table sf006 parallel 1;
Table altered.
这个问题给我们的启示是:并行并不总能够带来性能提升。




