使用GTT的过程在两个实例上具有不同的执行时间

时间:2013-06-27 12:32:18

标签: oracle oracle10g

我在DB(Oracle版本10g)中有一个有趣的问题。

有2个实例INS1INS2。在SC1架构中有一个包含2个过程的程序包,它从SC2架构调用,在INS1INS2个实例上都有一些参数。

第一个过程从使用GTT1创建的全局临时表on commit preserve rows中删除,然后插入到同一个中。

proc1(parameter_in) as
begin
    delete from gtt1;

    insert into gtt1
    from <other_table_1>
    where parameter=parameter_in;
end;

proc2(parameter_in) as
begin
    insert into <table_1>
    select * from gtt1, <other_table_1>
    where <join 2 tables>
    and parameter=parameter_in;

    -- again insert in same table with records not in <other_table_1>
    -- as they were deleted during refresh.

    insert into <table_1>
    select * from gtt1
    where not exists
        (row from <other_table_1>);

    delete from gtt1;            
end;

当从架构SC2一起调用它们时:

begin
    sc1.pkg1.proc1(parameter_in);
    sc1.pkg1.proc2(parameter_in);
end;
/

INS1上,它在10秒内执行。在INS2,需要3分钟。

当我在INS2上单独运行这些程序时,它会在5秒内运行:

begin
    sc1.pkg1.proc1(parameter_in);
end;
/

begin
    sc1.pkg1.proc2(parameter_in);
end;
/

我已检查过两个实例中表<other_table_1>中的统计数据和数据是否相同。

为什么在实例INS2上花费时间的任何建议?

1 个答案:

答案 0 :(得分:3)

我的猜测是,有人只收集了一个临时表的统计信息,导致只有一个系统出现错误的计划。

统计信息可能对临时表有害 - 由于数据非常不稳定,因此很难创建有代表性的统计信息。这就是为什么Oracle通常收集临时表的统计信息,而是使用动态采样来估计运行时的统计信息。例如,DBMS_STATS.GATHER_SCHEMA_STATS不会收集临时表的统计信息。但是DBMS_STATS.GATHER_TABLE_STATS会。

如果没有统计信息,则LAST_ANALYZED时间戳将为空:

select table_name, last_analyzed from dba_tables where table_name = 'GTT1';

如果是这种情况,请删除统计信息并锁定表格,以便不再发生这种情况。

begin
    dbms_stats.delete_table_stats(user, 'GTT1');
    dbms_stats.lock_table_stats(user, 'GTT1');
end;
/

现在,如果有人试图收集统计信息,他们会收到错误消息:

begin
    dbms_stats.gather_table_stats(user, 'GTT1');
end;
/

ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 2

如果您的系统依赖于动态采样,则需要确保参数已启用并合理设置。默认值2通常足够好:

select value from v$parameter where name = 'optimizer_dynamic_sampling'

这是一个脚本,演示了如何明确地收集统计数据会导致不良的基数估算。

创建表格,用100K行填充它们。只收集其中一个表的统计信息:

create global temporary table test_with_stats3(a number) on commit preserve rows;
create global temporary table test_no_stats3(a number) on commit preserve rows;

insert into test_with_stats3 select level from dual connect by level <= 100000;
insert into test_no_stats3 select level from dual connect by level <= 100000;
commit;

begin
    dbms_stats.gather_schema_stats(user);
end;
/

begin
    dbms_stats.gather_table_stats(user, 'TEST_WITH_STATS3');
end;
/

删除所有行。对于包含统计信息的表,Oracle仍然认为它有100K行:

delete from test_with_stats3;
delete from test_no_stats3;
commit;

explain plan for select * from test_with_stats3;
select * from table(dbms_xplan.display);

Plan hash value: 467959123

--------------------------------------------------------------------------------------
| Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                  |   100K|   488K|    44   (3)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST_WITH_STATS3 |   100K|   488K|    44   (3)| 00:00:01 |
--------------------------------------------------------------------------------------

没有统计数据的表格有更准确的估算值:

explain plan for select * from test_no_stats3;
select * from table(dbms_xplan.display);

Plan hash value: 2315614086

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |    13 |    43   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST_NO_STATS3 |     1 |    13 |    43   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement (level=2)