【问题标题】:Can Multiple Indexes Work Together?多个索引可以一起工作吗?
【发布时间】:2010-09-14 00:38:26
【问题描述】:

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

现在假设我执行诸如SELECT * FROM sometable WHERE foo='hello' AND bar='world'; 之类的查询,我的表中有大量的行,其中 foo 是“hello”,而少数行是“世界”。

因此,数据库服务器在底层做的最有效的事情是使用 bar 索引来查找 bar 为 'world' 的所有字段,然后仅返回 foo 为 'hello' 的那些行。这是O(n),其中 n 是 bar 为“世界”的行数。

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

那么,Oracle 是否足够聪明,可以在这里高效搜索?其他数据库呢?或者有什么方法可以在我的查询中告诉它以正确的顺序搜索?或许通过将bar='world' 放在WHERE 子句的首位?

【问题讨论】:

    标签: database oracle optimization indexing


    【解决方案1】:

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

    此外,Oracle 可以通过多种方式结合使用这两个索引——它可以将 btree 索引转换为位图并对它们执行位图 AND 操作,或者它可以对两者返回的 rowid 执行哈希连接索引。

    这里的一个重要考虑因素可能是被查询的值之间的任何相关性。如果 foo='hello' 占表中 80% 的值,bar='world' 占 10%,那么 Oracle 将估计查询将返回 0.8*0.1= 8% 的表行。但是,这可能不正确 - 查询实际上可能返回 10% 的 rwo 甚至 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)
    /
    

    【讨论】:

    • 请尊重 David,看看下面 Eli 的 cmets,他的问题的答案是“使用位图”。
    • 位图索引在这种类型的查询中肯定是有效的,但在 OLTP 环境中它们确实不友好。还值得知道的是,可以在位图操作中组合一对 btree 索引,尽管这样做的开销要大得多。
    【解决方案2】:

    伊莱,

    在你写的评论中:

    不幸的是,我有一个表,其中有很多列,每个列都有自己的索引。用户可以查询任意字段组合,因此我无法高效地为每个字段组合创建索引。但是,如果我确实只有两个需要索引的字段,我完全同意您使用两个索引的建议。 – Eli Courtwright(9 月 29 日 15:51)

    这实际上是相当重要的信息。有时,程序员在提问时会比自己聪明。他们试图将问题提炼成最重要的问题,但往往过于简化而错过了最佳答案。

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

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

    【讨论】:

    • 我阅读了所有的 cmets 并想知道为什么没有人说这正是位图被发明的原因。 +1
    • 感谢您的信息;我以前从未听说过位图索引,所以我会研究它们。在这个项目中更改我们当前的索引设计可能为时已晚,但如果我们遇到性能问题,那么我会回到 BMI 并肯定会尝试在未来的项目中使用它们。
    【解决方案3】:

    是的,您可以通过查询向 Oracle 提供“提示”。这些提示伪装成数据库的 cmets ("/* HINT */") 并且主要是特定于供应商的。因此,一个数据库的一个提示将不适用于另一个数据库。

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

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

    CREATE INDEX IX_BAR_AND_FOO on sometable(bar,foo);
    

    这种方式数据检索应该非常快。如果连接是唯一的,您只需创建一个应该快如闪电的唯一索引。

    【讨论】:

    • Informix 也有这些提示子句。大多数情况下,您不会以这种方式帮助优化器——它的作用非常好。
    • 不幸的是,我有一个表有很多列,每个列都有自己的索引。用户可以查询任意字段组合,因此我无法高效地为每个字段组合创建索引。但如果我确实只有两个字段需要索引,我完全同意你使用两个索引的建议。
    • 不要试图道歉:)。在您的情况下,Oracle 很可能会使用“最敏感”。同样,您不能只依赖 Oracle 的优化。但是,一方面,更新说明计划并尝试使其保持最新是一个好主意。
    【解决方案4】:

    首先,我假设您说的是好的、正常的、标准的 b*-tree 索引。位图索引的答案完全不同。 Oracle 中有很多针对各种类型索引的选项,这些选项可能会也可能不会改变答案。

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

    Oracle 甚至可以将两个 b*-tree 索引动态转换为位图,并将位图组合起来,以便使用这两个索引来查找需要检索的行。但这是一个相当不寻常的查询计划,尤其是在只有两列且其中一列具有高度选择性的情况下。

    【讨论】:

    • 积分。有人会认为数据库优化器默认会比较大小。
    【解决方案5】:

    Oracle 是否足够聪明,可以搜索 在这里高效吗?

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

    【讨论】:

      【解决方案6】:

      我相信你也可以让 Oracle 显示一个查询计划,这样你就可以准确地看到首先使用了哪个索引。

      【讨论】:

      • “计划”就是这样,它计划首先做什么。有时这会偏离实际发生的情况。您需要生成跟踪以准确了解发生了什么。
      【解决方案7】:

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

      【讨论】:

      • 其实我同意 Jeffrey 的观点……除了他所说的,拥有两个单独的索引会影响你的写入速度(因为数据库必须在写入时更新两个索引而不是一个。跨度>
      【解决方案8】:

      比那更好。

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

      【讨论】:

      • 首先,索引查找总是比全表扫描快是不正确的。在 Oracle 中,如果您要检索的行不止一小部分,则全表扫描的多块读取比索引的单块读取要快。
      • 其次,Oracle 优化器不会扫描这两个索引以确定使用哪个索引,它会使用数据字典中的统计信息来确定预期哪个索引更具选择性。这些统计数据将受到定义不同值选择性的直方图的影响。
      【解决方案9】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-09
        • 2016-01-29
        • 2017-02-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多