索引列上的值不同

时间:2013-09-23 14:20:45

标签: sql oracle indexing oracle11g query-optimization

我有一个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”需要多长时间呢?

提前感谢您的帮助

4 个答案:

答案 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几乎同时开始。通常他们应该几乎在同一时间完成。问题中的执行计划显示优化程序估计。活动报告的下方屏幕截图显示了非常类似查询的实际时间轴。所有步骤几乎在同一时间开始和停止。

enter image description here

合理计划的示例设置

下面是一个非常类似的设置,但配置非常简单。相同行数读取(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)

仔细查看解释计划。

  1. 它扫描整个索引以了解您要提取的内容
  2. 然后应用distinct函数(尝试检索唯一值)。虽然您说只有一个唯一值,但它必须扫描整个索引才能获得值。 Oracle不知道索引中只有一个不同的值。您可以限制rownum = 1以获得快速答案。
  3. 试试这个以获得快速回答

    select my_col from my_table where rownum = 1;
    

    在分布较少的列上添加索引是非常不利的。这对于表格和整个应用程序也是不利的。这没有任何意义