我在DB(Oracle版本10g)中有一个有趣的问题。
有2个实例INS1
和INS2
。在SC1
架构中有一个包含2个过程的程序包,它从SC2
架构调用,在INS1
和INS2
个实例上都有一些参数。
第一个过程从使用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
上花费时间的任何建议?
答案 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)