多个索引可以一起工作吗?

时间:2008-09-29 15:05:49

标签: database oracle optimization indexing

假设我有一个包含两个字段的数据库表,“foo”和“bar”。它们都不是唯一的,但每个都被编入索引。但是,它们不是被索引在一起,而是每个都有一个单独的索引。

现在假设我执行一个查询,例如SELECT * FROM sometable WHERE foo='hello' AND bar='world';我的表格中有大量的行,其中foo是'hello',而少数行的条形图是'world'。

因此,数据库服务器最有效的方法是使用bar索引查找bar为'world'的所有字段,然后仅返回foo为'hello'的那些行。这是O(n),其中n是bar为'world'的行数。

但是,我想这个过程可能会反过来,使用fo索引并搜索结果。这将是O(m),其中m是foo为'hello'的行数。

Oracle是否足够聪明,可以在这里高效搜索?其他数据库怎么样?或者有什么方法可以在我的查询中告诉它以正确的顺序搜索?也许将bar='world'置于WHERE条款的第一位?

9 个答案:

答案 0 :(得分:11)

Oracle几乎肯定会使用最具选择性的索引来驱动查询,您可以使用解释计划进行检查。

此外,Oracle可以通过两种方式组合使用这两个索引 - 它可以将btree索引转换为位图并对它们执行位图ANd操作,或者它可以对两个返回的rowid执行散列连接索引。

这里一个重要的考虑因素可能是被查询的值之间的任何相关性。如果foo ='hello'占表中值的80%且bar ='world'占10%,那么Oracle将估计查询将返回0.8 * 0.1 = 8%的表行。然而,这可能不正确 - 查询实际上可能返回10%的rwos甚至0%的行,具体取决于值的相关性。现在,根据整个表中这些行的分布,使用索引来查找它们可能效率不高。您可能仍需要访问(比方说)70%或表格块来检索所需的行(谷歌搜索“聚类因子”),在这种情况下,如果估计结果正确,Oracle将执行全表扫描。

在11g中,您可以收集多列统计数据,以帮助解决我认为的这种情况。在9i和10g中,您可以使用动态采样来非常好地估计要检索的行数。

要获得执行计划,请执行以下操作:

explain plan for
SELECT *
FROM   sometable
WHERE  foo='hello' AND bar='world'
/
select * from table(dbms_xplan.display)
/

与之对比:

explain plan for
SELECT /*+ dynamic_sampling(4) */
       *
FROM   sometable
WHERE  foo='hello' AND bar='world'
/
select * from table(dbms_xplan.display)
/

答案 1 :(得分:3)

是的,您可以向Oracle提供查询“提示”。这些提示伪装成数据库的注释(“/ * HINT * /”),主要是供应商特定的。因此,对一个数据库的一个提示将无法在其他数据库上运行。

我会在这里使用索引提示,这是小表的第一个提示。请参阅here

另一方面,如果您经常搜索这两个字段,为什么不在这两个字段上创建索引?我没有正确的语法,但它会像

CREATE INDEX IX_BAR_AND_FOO on sometable(bar,foo);

这样数据检索应该非常快。如果连接是唯一的,你只需创建一个应该闪电般快速的唯一索引。

答案 2 :(得分:3)

以利,

在你写的评论中写道:

  

不幸的是,我有一个包含许多列的表,每个列都有自己的索引。用户可以查询任何字段组合,因此无法在每个字段组合上有效地创建索引。但如果我只有两个字段需要索引,我完全同意你的建议使用两个索引。 - Eli Courtwright(9月29日15:51)

这实际上是非常重要的信息。有时程序员在提问时会智胜自己。他们试图将问题提炼到开创性的观点,但往往过于简化并错过了最佳答案。

这种情况正是发明位图索引的原因 - 处理在where子句中使用未知的列组的时间。

以防万一有人说BMI仅适用于低基数列,可能不适用于您的情况。低可能不像你想象的那么小。唯一真正的问题是DML与表的并发性。必须是单线程或罕见的才能工作。

答案 3 :(得分:2)

  

Oracle也足够智能搜索   在这里有效吗?

简单的答案是“可能”。致力于优化查询优化器的每个数据库供应商都有很多非常聪明的人,所以它可能正在做你甚至没想过的事情。如果你更新统计数据,它可能会做更多。

答案 4 :(得分:2)

首先,我假设您正在讨论漂亮,正常,标准的b * -tree索引。位图索引的答案完全不同。对于Oracle中的各种类型的索引,有很多选项可能会也可能不会改变答案。

至少,如果优化器能够确定特定条件的选择性,它将使用更具选择性的索引(即条形索引)。但是如果你有偏斜的数据(列栏中有N个值,但任何特定值的选择性基本上大于或小于数据的1 / N),你需要在列上有一个直方图才能告诉优化器哪些值或多或少可能。如果您正在使用绑定变量(正如所有优秀的OLTP开发人员所应该的那样),根据Oracle版本的不同,您可能会遇到绑定变量偷看的问题。

Oracle可能甚至可以将两个b * -tree索引实时转换为位图并组合位图,以便使用这两个索引来查找需要检索的行。但这是一个相当不寻常的查询计划,特别是如果只有两列,其中一列具有高选择性。

答案 5 :(得分:1)

我确信您也可以让Oracle显示查询计划,以便您可以确切地看到首先使用的索引。

答案 6 :(得分:1)

您可以提供有关使用哪个索引的提示。我不熟悉Oracle,但在Mysql中你可以使用USE | IGNORE | FORCE_INDEX(有关详细信息,请参阅here)。为了获得最佳性能,您应该使用组合索引。

答案 7 :(得分:1)

最好的方法是将foo添加到条形索引,或者将条形添加到foo的索引(或两者)。如果foo的索引还包含条形索引,那么额外的索引级别不会影响foo索引在该索引的任何当前使用中的效用,也不会明显影响维护该索引的性能,但它会给数据库增加额外的用于优化查询的信息,例如示例。

答案 8 :(得分:1)

这比那更好。

Index Seeks总是比全表扫描更快。因此,幕后Oracle(以及SQL服务器)将首先在两个索引上找到行的范围。然后它将查看哪个范围更短(看到它是一个内部连接),它将迭代较短的范围以找到与两者中较大范围的匹配。

相关问题