Oracle Date索引很慢。没有它,查询速度要快300倍

时间:2014-03-21 07:02:38

标签: performance oracle date indexing query-optimization

我有一个Oracle查询,如下所示需要10分钟或更长时间才能运行:

  select
      r.range_text as duration_range,
      nvl(count(c.call_duration),0) as calls,
      nvl(SUM(call_duration),0) as total_duration
      from
      call_duration_ranges r
      left join
      big_table c
      on c.call_duration BETWEEN r.range_lbound AND r.range_ubound
 and c.aaep_src = 'MAIN_SOURCE'
 and c.calltimestamp_local  >= to_date('01-02-2014 00:00:00' ,'dd-MM-yyyy HH24:mi:ss')
 AND c.calltimestamp_local <=  to_date('28-02-2014 23:59:59','dd-MM-yyyy HH24:mi:ss')
 and          c.destinationnumber LIKE substr( 'abc:1301@company.com:5060;user=phone',1,8) || '%'    
 group by
      r.range_text
 order by
      r.range_text

如果我将查询的日期部分更改为:

 (c.calltimestamp_local+0)  >= to_date('01-02-2014 00:00:00' ,'dd-MM-yyyy HH24:mi:ss')
 (AND c.calltimestamp_local+0) <=  to_date('28-02-2014 23:59:59','dd-MM-yyyy HH24:mi:ss')

它在2秒内运行。我基于另一篇文章做了这个,以避免使用日期索引。虽然看起来很直观 - 索引使事情变得如此之慢。

执行解释计划,新查询和更新查询之间似乎相同。唯一的区别是MERGE JOIN操作在旧查询中是16,269字节,在新查询中是1,218字节。实际上,旧查询中的基数也更高。而我实际上并没有看到&#34; INDEX&#34;对解释计划中的旧查询或新查询的操作,仅针对destinationnumber字段上的索引。

那么为什么索引如此放慢了查询的速度呢?我能对指数做些什么 - 不要以为使用&#34; + 0&#34;是最好的解决方案......

查询两天的数据,禁止使用destinationnumber索引:

0   SELECT STATEMENT            ALL_ROWS    329382  1218    14
1   SORT    GROUP BY            329382  1218    14
2   MERGE JOIN  OUTER           329381  1218    14
3   SORT    JOIN            4   308 14
4   TABLE ACCESS    FULL    CALL_DURATION_RANGES    ANALYZED    3   308 14
5   FILTER                      
6   SORT    JOIN            329377  65  1
7   TABLE ACCESS    BY GLOBAL INDEX ROWID   BIG_TABLE   ANALYZED    329376  65  1
8   INDEX   RANGE SCAN  IDX_CDR_CALLTIMESTAMP_LOCAL ANALYZED    1104        342104

使用destinationnumber索引查询2天:

0   SELECT STATEMENT            ALL_ROWS    11  1218    14
1   SORT    GROUP BY            11  1218    14
2   MERGE JOIN  OUTER           10  1218    14
3   SORT    JOIN            4   308 14
4   TABLE ACCESS    FULL    CALL_DURATION_RANGES    ANALYZED    3   308 14
5   FILTER                      
6   SORT    JOIN            6   65  1
7   TABLE ACCESS    BY GLOBAL INDEX ROWID   BIG_TABLE   ANALYZED    5   65  1
8   INDEX   RANGE SCAN  IDX_DESTINATIONNUMBER_PART  ANALYZED    4       4

查询一个月,抑制destinationnumber索引 - 完全扫描:

0   SELECT STATEMENT            ALL_ROWS    824174  1218    14
1   SORT    GROUP BY            824174  1218    14
2   MERGE JOIN  OUTER           824173  1218    14
3   SORT    JOIN            4   308 14
4   TABLE ACCESS    FULL    CALL_DURATION_RANGES    ANALYZED    3   308 14
5   FILTER                      
6   SORT    JOIN            824169  65  1
7   PARTITION RANGE ALL         824168  65  1
8   TABLE ACCESS    FULL    BIG_TABLE   ANALYZED    824168  65  1

2 个答案:

答案 0 :(得分:2)

  

虽然看起来很直观 - 索引减慢了很多东西。

只有在您不了解索引的工作方式时才会出现反直觉。

索引适用于检索单个行。它们不适合检索大量记录。您还没有提供任何指标,但您的查询似乎可能涉及大量行。在这种情况下,全表扫描或其他基于set =的操作将更有效。


调整日期范围查询很棘手,因为无论我们的统计数据是多么最新,数据库都很难知道两个边界之间有多少记录。 (当日期界限可能变化时调整甚至更棘手 - 一天与一个月或一年不同。)因此,我们经常需要通过使用我们对数据的了解来帮助优化器。

  

不要使用&#34; + 0&#34;是最好的解决方案......

为什么不呢?人们一直在使用这种技术来避免在几十年内在特定查询中使用索引。

然而,还有更现代的解决方案。未记录的基数提示是一个:

 select /*+ cardinality(big_table,10000) */ 

...应该足以阻止优化器使用索引 - 只要您为查询中的表所有收集了准确的统计信息。

或者,您可以强制优化器使用...

进行全表扫描
 select /*+ full(big_table) */ 

无论如何,你无法对索引做任何改变数据库工作方式的事情。您可以通过分区来加快速度,但我猜您的组织是否已经购买了Partitioning option您已经使用过它。

答案 1 :(得分:2)

这些是使用索引减慢查询速度的原因:

  1. 完整的表扫描会更快。如果必须检索大部分行,则会发生这种情况。具体数字取决于各种因素,但如果您检索超过10-20%的行,使用索引的常见情况下的经验法则较慢。

  2. 使用其他索引会更好,因为在第一阶段之后剩下的行数会减少。在表上使用某个索引通常意味着不能使用其他索引。

  3. 现在决定哪种变体是最好的是优化工作。要执行此任务,他必须猜测(除其他事项外)应用某些过滤子句后剩余多少行。此估计值基于表统计信息,通常很好。它甚至会考虑到偏斜的数据,但如果您的statitiscs过时或者您的数据分布相当罕见,它可能会关闭。例如,如果您在示例中插入了二月的数据之前计算了统计信息,则优化程序可能会错误地断定在应用日期范围过滤器后只剩下很少(如果有)行。

    在多个列上使用组合索引也可能是一个取决于您的数据的选项。

    关于&#34;偏斜数据问题的另一个注意事项&#34;:如果您在cloumn A上有索引,优化程序会检测到A列中的偏斜数据,但如果您在A列和A列上只有一个组合索引则不会B,因为组合可能使分布更均匀。这是A,B上的索引没有在A冗余上建立索引的少数情况之一。

    APC答案显示如果使用正确的统计数据仍然会产生错误的计划,如何使用提示将优化器指向正确的方向。