我有一个115 M行的表。其中一列被索引(下面的解释计划中称为“my_index”的索引)并且不可为空。此外,到目前为止,该专栏只有一个不同的价值。
当我这样做时
select distinct my_col from my_table;
,需要230秒,这是非常长的。这是解释计划。
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 3 | 22064 (90)| 00:03:23 |
| 1 | SORT UNIQUE NOSORT| | 1 | 3 | 22064 (90)| 00:03:23 |
| 2 | INDEX FULL SCAN | my_index | 115M| 331M| 2363 (2)| 00:00:22 |
由于该列只有一个不同的值,为什么需要这么长时间?为什么Oracle不只是检查索引条目并快速发现此列只有一个可能的值?在上面的解释计划中,索引扫描似乎需要22秒,但这个“SORT UNIQUE NOSORT”需要多长时间呢?
提前感谢您的帮助
答案 0 :(得分:3)
重新分析表格。
EXEC dbms_stats.gather_table_stats('owner','table_name',cascade=>true,method_opt=>'FOR ALL INDEXED COLUMNS SIZE ');
更改索引类型
115M行中有一个不同的值?? !!这就是所谓的低基数,对于'普通'B树索引不太好考虑一个位图索引。 (如果你有B树的话)
重建查询
如果您确定不会在此列中添加新值,请删除distinct子句,而不是像Abhijith所说的那样使用。
答案 1 :(得分:3)
SORT UNIQUE NOSORT
不会花太长时间。您正在查看错误执行计划的估计值,这可能是不合理的优化器参数的结果。例如,将参数OPTIMIZER_INDEX_COST_ADJ设置为1而不是默认值100可以生成类似的计划。很可能您的查询运行缓慢,因为您的数据库很忙或只是很慢。
发布的执行计划有什么问题?
发布的执行计划似乎不合理。检索数据应该比简单地丢弃重复数据花费更长的时间。消费者操作SORT UNIQUE NOSORT
可以与生产者操作INDEX FULL SCAN
几乎同时开始。通常他们应该几乎在同一时间完成。问题中的执行计划显示优化程序估计。活动报告的下方屏幕截图显示了非常类似查询的实际时间轴。所有步骤几乎在同一时间开始和停止。
合理计划的示例设置
下面是一个非常类似的设置,但配置非常简单。相同行数读取(1.15亿)并返回(1),几乎完全相同的段大小(329MB对331 MB)。该计划几乎将所有时间花在了INDEX FULL SCAN
上。
drop table test1 purge;
create table test1(a number not null, b number, c number) nologging;
begin
for i in 1 .. 115 loop
insert /*+ append */ into test1 select 1, level, level
from dual connect by level <= 1000000;
commit;
end loop;
end;
/
create index test1_idx on test1(a);
begin
dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
explain plan for select /*+ index(test1) */ distinct a from test1;
select * from table(dbms_xplan.display);
Plan hash value: 77032494
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 244K (4)| 00:48:50 |
| 1 | SORT UNIQUE NOSORT| | 1 | 3 | 244K (4)| 00:48:50 |
| 2 | INDEX FULL SCAN | TEST1_IDX | 115M| 329M| 237K (1)| 00:47:30 |
--------------------------------------------------------------------------------
重新制定糟糕的计划
--Set optimizer_index_cost_adj to a ridiculously low value.
--This changes the INDEX FULL SCAN estimate from 47 minutes to 29 seconds.
alter session set optimizer_index_cost_adj = 1;
--Changing the CPUSPEEDNW to 800 will exactly re-create the time estimate
--for SORT UNIQUE NOSORT. This value is not ridiculous, and it is not
--something you should normally change. But it does imply your CPUs are
--slow. My 2+ year-old desktop had an original score of 1720.
begin
dbms_stats.set_system_stats( 'CPUSPEEDNW', 800);
end;
/
explain plan for select /*+ index(test1) */ distinct a from test1;
select * from table(dbms_xplan.display);
Plan hash value: 77032494
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 16842 (86)| 00:03:23 |
| 1 | SORT UNIQUE NOSORT| | 1 | 3 | 16842 (86)| 00:03:23 |
| 2 | INDEX FULL SCAN | TEST1_IDX | 115M| 329M| 2389 (2)| 00:00:29 |
--------------------------------------------------------------------------------
如何调查
检查参数。
select name, value from v$parameter where name like 'optimizer_index%'
NAME VALUE
---- -----
optimizer_index_cost_adj 1
optimizer_index_caching 0
同时检查系统统计信息。
select * from sys.aux_stats$;
+---------------+------------+-------+------------------+
| SNAME | PNAME | PVAL1 | PVAL2 |
+---------------+------------+-------+------------------+
| SYSSTATS_INFO | STATUS | | COMPLETED |
| SYSSTATS_INFO | DSTART | | 09-23-2013 17:52 |
| SYSSTATS_INFO | DSTOP | | 09-23-2013 17:52 |
| SYSSTATS_INFO | FLAGS | 1 | |
| SYSSTATS_MAIN | CPUSPEEDNW | 800 | |
| SYSSTATS_MAIN | IOSEEKTIM | 10 | |
| SYSSTATS_MAIN | IOTFRSPEED | 4096 | |
| SYSSTATS_MAIN | SREADTIM | | |
| SYSSTATS_MAIN | MREADTIM | | |
| SYSSTATS_MAIN | CPUSPEED | | |
| SYSSTATS_MAIN | MBRC | | |
| SYSSTATS_MAIN | MAXTHR | | |
| SYSSTATS_MAIN | SLAVETHR | | |
+---------------+------------+-------+------------------+
要找出实际花费的时间,请使用活动报告之类的工具。
select dbms_sqltune.report_sql_monitor(sql_id => '5s63uf4au6hcm',
type => 'active') from dual;
答案 2 :(得分:1)
如果列只有几个不同的值,请尝试压缩索引:
create index my_index on my_table (my_col) compress;
这将只存储一次列的每个不同值,希望减少查询的执行时间。
作为奖励:使用此功能查看用于查询的实际计划:
select /*+ gather_plan_statistics */ distinct my_col from my_table;
SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR);
gather_plan_statistics提示将收集更多数据(执行时间会更长),但它也可以不用它。有关详细信息,请参阅DBMS_XPLAN.DISPLAY_CURSOR的文档。
答案 3 :(得分:0)
仔细查看解释计划。
试试这个以获得快速回答
select my_col from my_table where rownum = 1;
在分布较少的列上添加索引是非常不利的。这对于表格和整个应用程序也是不利的。这没有任何意义