4

我试图了解 no_index 实际上如何加快查询速度,但无法在线找到文档来解释它。

例如,我有一个运行非常慢的查询

select  * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2)

我们的一位 DBA 能够通过这样做显着加快速度

select  /*+ NO_INDEX(TAB_000000000019) */ * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2) 

我不知道为什么?我想弄清楚为什么它会起作用,所以我可以看看我是否可以将它应用到另一个查询(这个是一个连接)以加快它的速度,因为它需要更长的时间来运行。

谢谢!


** 更新 ** 以下是我对示例中表格的了解。

  • 这是一个“分区表”
  • TAB_000000000019 是表而不是其中的列
  • field1 被索引
4

6 回答 6

9

Oracle 的优化器判断如何最好地运行查询,为此它使用收集的有关表和索引的大量统计信息。例如,根据这些统计信息,它决定是否使用索引,或者只进行表扫描。

至关重要的是,这些统计数据不会自动更新,因为收集它们可能非常昂贵。在统计信息不是最新的情况下,优化器可能会做出“错误”的决定,并且可能在执行表扫描实际上更快时使用索引。

如果 DBA/开发人员知道这一点,他们可以向NO_INDEX优化器提供提示(这是什么),告诉它不要使用给定的索引,因为众所周知它会减慢速度,通常是由于过时的统计信息。

在您的示例中,TAB_000000000019将引用索引或表(我猜是索引,因为它看起来像一个自动生成的名称)。

老实说,这有点像黑色艺术,但这就是它的要点,据我所知。

免责声明:我不是 DBA,但我涉足该领域。

于 2011-01-20T17:49:19.447 回答
3

根据您的更新:如果 field1 是唯一的索引字段,则原始查询可能会对该索引进行快速全面扫描(即读取索引中的每个条目并检查 field1 上的过滤条件),然后使用这些结果查找表中的行并根据其他条件进行过滤。field1 上的条件使得索引唯一扫描或范围扫描(即在索引中查找特定值或值范围)是不可能的。

优化器可能选择了这条路径,因为 field1 上有两个过滤谓词。优化器将计算其中每一个的估计选择性,然后将它们相乘以确定它们的组合选择性。但在许多情况下,这会大大低估符合条件的行数。

NO_INDEX 提示从优化器的考虑中消除了此选项,因此它基本上与它认为次佳的计划相一致——在这种情况下,可能使用基于查询中其他过滤条件之一的分区消除。

于 2011-01-21T13:59:56.340 回答
2

关于索引需要注意的是,它们是根据行顺序和字段中的数据预先计算的值。在这种特定情况下,您说 field1 已编入索引,并且您在查询中使用它,如下所示:

    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString'

在上面的查询片段中,过滤器位于可变数据段上,因为百分比 (%) 字符包含字符串,然后位于另一个特定字符串上。这意味着不使用优化器提示的默认 Oracle 优化将首先尝试在索引字段中查找字符串,并查找数据是否是字段中数据的子字符串,然后检查数据与另一个特定字符串不匹配。检查索引后,将检查其他列。如果重复,这是一个非常缓慢的过程。

DBA 提出的 NO_INDEX 提示消除了优化器对使用索引的偏好,并且可能允许优化器首先选择更快的比较,而不必先强制进行索引比较,然后再比较其他列。

以下内容很慢,因为它比较了字符串及其子字符串:

            field1_ like '%someGenericString%'

虽然以下是更快的,因为它是特定的:

            field1_ like 'someSpecificString'

因此,使用 NO_INDEX 提示的原因是,如果您对索引进行比较会减慢速度。如果将索引字段与更具体的数据进行比较,则索引比较通常更快。

我说通常是因为当索引字段包含更多冗余数据时,例如上面提到的@Atish 示例,在返回正比较之前,它必须经过一长串比较负数。提示会产生不同的结果,因为数据库设计和表中的数据都会影响查询的执行速度。因此,为了应用提示,您需要知道您提示给优化器的各个比较是否会在您的数据集上更快。这个过程没有捷径可走。应用提示应该在编写适当的 SQL 查询之后发生,因为提示应该基于真实数据。

查看此提示参考:http ://docs.oracle.com/cd/B19306_01/server.102/b14211/hintsref.htm

于 2012-06-29T10:02:46.270 回答
2

如果与使用索引查询表相比,使用索引会导致更多磁盘 IO,则会降低查询性能。

这可以用一个简单的表格来证明:

create table tq84_ix_test (
  a number(15) primary key,
  b varchar2(20),
  c number(1)
);

以下块将 100 万条记录填充到此表中。在 b 列中,每 250 条记录都用 a 填充,rare value而所有其他记录都用 填充frequent value

declare
  rows_inserted number := 0;
begin

  while rows_inserted < 1000000  loop

        if mod(rows_inserted, 250) = 0 then

           insert into tq84_ix_test values (
               -1 * rows_inserted, 
               'rare value',
                1);

            rows_inserted := rows_inserted + 1;

        else

           begin
              insert into tq84_ix_test values (
                 trunc(dbms_random.value(1, 1e15)),
                'frequent value',
                 trunc(dbms_random.value(0,2))
               );
               rows_inserted := rows_inserted + 1;

           exception when dup_val_on_index then 
               null;
           end;

        end if;

  end   loop;

end;
/

将索引放在列上

create index tq84_index on tq84_ix_test (b);

相同的查询,但一次有索引,一次没有索引,性能不同。自己检查一下:

set timing on


select /*+ no_index(tq84_ix_test) */
    sum(c)
  from 
    tq84_ix_test
  where
    b = 'frequent value';


select /*+ index(tq84_ix_test tq84_index) */
    sum(c)    
  from 
    tq84_ix_test
  where
    b = 'frequent value';

为什么?在没有索引的情况下,按顺序读取所有数据库块。通常,这是昂贵的,因此被认为是不好的。在正常情况下,使用索引,这样的“全表扫描”可以减少为读取 2 到 5 个索引数据库块加上读取包含索引指向的记录的一个数据库块。对于此处的示例,它完全不同:读取整个索引,并且对于索引中的(几乎)每个条目,也读取数据库块。因此,不仅要读取整个表,还要读取索引。请注意,如果c索引中也存在,则此行为会有所不同,因为在这种情况下,Oracle 可以选择c从索引中获取值,而不是绕道访问表。

所以,概括一下这个问题:如果索引没有选择很少的记录,那么不使用它可能是有益的。

于 2011-01-20T20:36:52.553 回答
0

我曾在某处读到,在查询前使用 %,如 '%someGenericString%' 会导致 Oracle 忽略该字段上的 INDEX。也许这解释了为什么查询运行缓慢。

于 2013-02-06T11:18:32.737 回答
0

补充一下 Rene' 和 Dave 所说的,这是我在生产环境中实际观察到的:

如果索引字段上的条件返回太多匹配项,Oracle 最好进行全表扫描。

我们有一个报表程序查询一个非常大的索引表——索引在一个区域代码上,并且查询指定了确切的区域代码,因此 Oracle CBO 使用该索引。

不幸的是,一个特定的区域代码占表格条目的 90%。

只要针对其他(次要)区域代码之一运行报告,它就会在不到 30 分钟内完成,但对于主要区域代码则需要很多小时。

向 SQL 添加提示以强制进行全表扫描解决了该问题。

希望这可以帮助。

于 2011-01-21T23:17:24.467 回答