有时候在执行sql的时候,为了加快执行的速度,可能会采取并发的方式去执行。为了减少影响,通常都是通过加parallel hint 或者alter session force parallel QUERY parallel ;但是可能sql并未按照预期的那样去进行并发处理
11.2.0.4
1 2 3 4 5 6 7 8 9 10 11 SYS@xb> create table t as select * from dba_objects; Table created. SYS@xb> create index idx_id on t(owner); Index created. SYS@xb> exec dbms_stats.gather_table_stats(user,'T',method_opt=>'for all columns size 1',cascade=>true); PL/SQL procedure successfully completed.
加上并发hint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 SYS@xb> select /*+ parallel(t 4)*/ count(*) from t where owner like 'S%'; COUNT(*) ---------- 41973 SYS@xb> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST +PEEKED_BINDS +PARALLEL +PARTITION +COST +BYTES')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- SQL_ID 6va6j0s526fc1, child number 0 ------------------------------------- select /*+ parallel(t 4)*/ count(*) from t where owner like 'S%' Plan hash value: 2157616264 ------------------------------------------------------------------ | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| ------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 2 (100)| | 1 | SORT AGGREGATE | | 1 | 6 | | |* 2 | INDEX RANGE SCAN| IDX_ID | 7360 | 44160 | 2 (0)| ------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OWNER" LIKE 'S%') filter("OWNER" LIKE 'S%')
可以看到虽然我在sql里加上了/*+ parallel(t 4)*/
,但是执行计划并未采用并发,稍微把sql做下改动,去掉where条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 SYS@xb> select /*+ parallel(t 4)*/ count(*) from t; COUNT(*) ---------- 86353 SYS@xb> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST +PEEKED_BINDS +PARALLEL +PARTITION +COST +BYTES')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- SQL_ID 7nh87asa88rb5, child number 0 ------------------------------------- select /*+ parallel(t 4)*/ count(*) from t Plan hash value: 3126468333 ---------------------------------------------------------------------------------------------- | Id | Operation | Name | E-Rows | Cost (%CPU)| TQ |IN-OUT| PQ Distrib | ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 93 (100)| | | | | 1 | SORT AGGREGATE | | 1 | | | | | | 2 | PX COORDINATOR | | | | | | | | 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | | Q1,00 | P->S | QC (RAND) | | 4 | SORT AGGREGATE | | 1 | | Q1,00 | PCWP | | | 5 | PX BLOCK ITERATOR | | 86353 | 93 (0)| Q1,00 | PCWC | | |* 6 | TABLE ACCESS FULL| T | 86353 | 93 (0)| Q1,00 | PCWP | | ---------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 6 - access(:Z>=:Z AND :Z>=:Z)
这个时候的执行计划变成跟我预期的一样了,这是什么原理,parallel的hint怎么时好时坏。
做10053trace
select /*+ parallel(t 4)*/ count(*) from t
1 2 3 4 5 6 7 8 9 10 SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for T[T] Table: T Alias: T Card: Original: 86353.000000 Rounded: 86353 Computed: 86353.00 Non Adjusted: 86353.00 Access Path: TableScan Cost: 335.35 Resp: 93.15 Degree: 0 Cost_io: 335.00 Cost_cpu: 12952950 Resp_io: 93.06 Resp_cpu: 3598042 Best:: AccessPath: TableScan Cost: 93.15 Degree: 4 Resp: 93.15 Card: 86353.00 Bytes: 0
这里优化器认为全表扫描的成本最低,所以选择了最好的tablescan,并发度为4
select /*+ parallel(t 4)*/ count(*) from t where owner like ‘S%’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for T[T] Column (#1): OWNER( AvgLen: 6 NDV: 24 Nulls: 0 Density: 0.041667 Table: T Alias: T Card: Original: 86353.000000 Rounded: 7360 Computed: 7360.20 Non Adjusted: 7360.20 Access Path: TableScan Cost: 335.59 Resp: 93.22 Degree: 0 Cost_io: 335.00 Cost_cpu: 21588250 Resp_io: 93.06 Resp_cpu: 5996736 Access Path: index (index (FFS)) Index: IDX_ID resc_io: 57.00 resc_cpu: 20443312 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Access Path: index (FFS) Cost: 57.55 Resp: 57.55 Degree: 1 Cost_io: 57.00 Cost_cpu: 20443312 Resp_io: 57.00 Resp_cpu: 20443312 kkofmx: index filter:"T"."OWNER" LIKE 'S%' Access Path: index (IndexOnly) Index: IDX_ID resc_io: 19.00 resc_cpu: 1607507 ix_sel: 0.085234 ix_sel_with_filters: 0.085234 Cost: 1.90 Resp: 1.90 Degree: 1 Best:: AccessPath: IndexRange Index: IDX_ID Cost: 1.90 Degree: 1 Resp: 1.90 Card: 7360.20 Bytes: 0
这里可以看到优化器列出了多种不同的访问路径和并发度的情形,主要包含tablescan,index ffs, index range scan,由于parallel hint只对tablescan生效,Cost_io: 335.00表示串行的io消耗,Resp_io: 93.06表示并发的响应消耗,cost_io / (0.9 * degree),这里显示是Degree: 0,但实际上应该是4
根据以上可以判断,当对语句加上parallel hint的时候,优化器并不是必须就会采取并发的方式来执行,而是先计算出并发全表扫描的cost,然后计算index ffs和index range scan的cost,比较以后选择cost最低(Cost: 1.90)的那一种执行计划