【问题标题】:Improve oracle query performance without indexing在不建立索引的情况下提高 oracle 查询性能
【发布时间】:2010-09-19 15:14:12
【问题描述】:

在不创建索引的情况下,我可以做些什么来提高 oracle 查询的查询性能?

这是我试图加快运行速度的查询:

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a,
itempages b,
keygroupdata c
WHERE a.ItemType IN (112,115,189,241)
AND a.ItemNum = b.ItemNum
AND b.ItemNum = c.ItemNum
ORDER BY a.DateStored DESC

这些列都没有索引,每个表都包含数百万条记录。不用说,执行查询需要 3 分半钟。这是生产环境中的第三方数据库,我不允许创建任何索引,因此必须对查询本身进行任何性能改进。

谢谢!

【问题讨论】:

  • 列的数据类型是什么?特别是 ItemType?
  • 计划是什么。每个表有多少行?如果您无法创建任何索引,那么您可能无能为力。

标签: sql performance oracle


【解决方案1】:

好吧,既然你不能创建索引,我会确保统计信息都是最新的,然后我会这样重写查询:

with a as (select /*+ MATERIALIZE */ ItemType, ItemNum, DateStored, ItemDate from items where ItemType in (112,115,189,241)) SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath FROM a, itempages b, keygroupdata c WHERE a.ItemNum = b.ItemNum AND b.ItemNum = c.ItemNum ORDER BY a.DateStored DESC

【讨论】:

    【解决方案2】:

    首先在这个查询上创建一个视图,然后从这个视图生成一个表。同时创建一个日期索引,创建一个作业并将其安排在系统空闲的午夜时间。

    【讨论】:

      【解决方案3】:

      我知道这个线程已经很老了,但是对于搜索引擎,我仍然想提供一个替代解决方案,它可以在 oracle 上运行,并且根据数据可能会更快。

      with a as (
        select 
          * 
        from 
          items 
        where 
          ItemType IN (112,115,189,241)
      )
      SELECT 
        c.ClaimNumber
        , a.ItemDate
        , c.DTN, b.FilePath
      FROM 
        a,
        itempages b,
        keygroupdata c
      WHERE 
        a.ItemNum = b.ItemNum
        AND b.ItemNum = c.ItemNum
      ORDER BY 
        a.DateStored DESC
      

      您也可以尝试WITH 子句中的/*+ MATERIALIZE */ 提示。

      其实我发现oracle的旧join语法比ansi sql更容易阅读^^

      【讨论】:

        【解决方案4】:

        如果你说没有索引,那么这是否也意味着没有定义主键或外键?显然,分析表和收集统计数据很重要,但如果不存在诸如定义表应该如何连接的元数据,那么 Oracle 很可能会选择一个糟糕的执行路径。

        在这种情况下,使用诸如 /*+ ORDERED */ 之类的提示很可能是使优化器可靠地选择良好执行路径的唯一选项。也可能值得添加外键和主键,但将它们定义为 DISABLE 和 VALIDATE。

        我想这条评论的有用性取决于对索引的厌恶程度有多远,所以 YMMV。

        【讨论】:

          【解决方案5】:

          是否在这些表上收集了统计信息?如果不是这样,收集统计数据可能会改变执行计划,尽管它不一定会变得更好。

          除此之外,看看执行计划。您可能会看到它以非最佳顺序加入表(例如,它可能在加入具有过滤条件的 a 之前加入 b 和 c)。

          您可以使用提示来尝试影响访问路径、连接顺序或连接方法。

          更新:回复评论让我转至this 演示文稿,这可能会有所帮助或至少很有趣。

          【讨论】:

          • 如果 OP 声明的表上没有任何索引,则统计信息没有用。他被全表扫描困住了。否则,好点。
          • 哎呀。 OP 只是说这些列没有索引,并不是说没有。统计数据实际上可能会有所帮助。但考虑到第三方应用程序的问题,打开它们可能仍然不是他的选择。
          • 仅表上的统计数据就可以产生影响。我感觉 Mogens Norgaard 有一篇关于这个的好论文,但我不记得我在哪里找到的。
          【解决方案6】:

          根据 ItemType 列的数据类型,如果它是 varchar,您可能会体验到更快的执行速度,Oracle 将执行隐式转换。

          SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
          FROM items a,
          itempages b,
          keygroupdata c
          WHERE ((a.ItemType IN ('112','115','189','241'))
          AND (a.ItemNum = b.ItemNum)
          AND (b.ItemNum = c.ItemNum))
          ORDER BY a.DateStored DESC
          

          【讨论】:

            【解决方案7】:

            有时,通过向 where 子句添加看似冗余的元素,添加额外的路径供优化器选择,您会看到好处。

            例如,您有 A.ItemNum = B.ItemNum 和 B.ItemNum = C.ItemNum。也尝试添加 A.ItemNum = C.ItemNum。不过,我很确定优化器足够聪明,可以自行解决这个问题——不过值得一试。

            【讨论】:

            • @David Aldridge:希望您对此表示赞同或反对 8)。我已经看到它适用于旧版本的 Oracle,但我不知道它是否仍然是一个可行的技巧
            • Oracle 确实加入了 transtivty - 自动添加 A.ItemNum = C.ItemNum (在 10g 以后确定)。它只为超出您指定的额外步骤执行此操作。这对这种情况没有帮助,但在其他一些情况下可以。
            • @WW。我最后一次听说这不适用于连接。平等是正确的。 A.id = myID 和 A.ID = B.ID ... Oracle 将添加 B.ID = myID 但不是连接。即使他们声称要这样做,我也通过添加连接获得了巨大的性能提升......所以它可能有能力但并非总是如此。
            【解决方案8】:

            这是您经常运行的查询吗?创建加快此查询所需的索引似乎符合数据库所有者的利益。您运行查询所花费的 3.5 分钟肯定会对他们的生产环境产生一些影响!

            另外,他们是否一直在对表运行更新统计信息?这可能会提高性能,因为连接顺序是根据表的统计信息计算的。

            顺便说一句,你可以做什么?刚读?如果您可以创建临时表并在其上放置索引,我可能会考虑制作表的临时副本,为这些表建立索引,然后对临时副本进行索引辅助连接。

            【讨论】:

              【解决方案9】:

              您可以在加入表格之前尝试过滤项目类型,如下所示。

              如果您在 9i 之前的 Oracle 上运行,这有时会带来意想不到的好处。

              select 
                c.claimnumber,
                a.itemdate, 
                c.dtn,
                b.filepath
              from 
                (
                select itemdate
                from items it
                where it.itemtype in(112,115,189,241)
                ) a
                itempages b,
                keygroupdata c
              where a.itemnum = b.itemnum
                and b.itemnum = c.itemnum
              

              您也可以尝试添加提示 /+RULE/ 或 /+ORDERED/ 以查看会发生什么...再次,尤其是旧版本,这些有时会给出令人惊讶的结果。

              SELECT /*+RULE*/
                c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
              FROM
                items a,
                itempages b,
                keygroupdata c
              WHERE a.ItemType IN (112,115,189,241)
                AND a.ItemNum = b.ItemNum
                AND b.ItemNum = c.ItemNum
              ORDER BY a.DateStored DESC
              

              【讨论】:

                【解决方案10】:

                首先,看一下执行计划。它是否准确反映了查询执行的每个阶段要检索的行数?谓词“a.ItemType IN (112,115,189,241)”的选择性如何?执行计划是否显示任何临时磁盘空间用于连接或排序?

                实际上,也许您可​​以修改问题以包含执行计划。

                还要确保您没有禁用散列连接,这在 OLTP 调整的系统中有时会出现这种情况,因为它们是在 Oracle 中等值连接批量数据的最有效方式。他们应该出现在执行计划中。

                【讨论】:

                  【解决方案11】:

                  如果查询输入是恒定的或可预测的(itemType IN (...)),那么另一种方法是每天运行一次或两次查询,并将结果存储在本地表中,并在适当的地方使用索引。

                  然后,您可以使代价高昂的查询“离线”,并为交互式查询获得更快/更好的结果。

                  【讨论】:

                    【解决方案12】:

                    您可能想尝试在这些表中的任何一个上创建实体化视图。然后,您可以在物化视图上创建一个索引,这将有助于加快查询速度(然后查询物化视图而不是原始表)。

                    当然,如果您的基础表已更新,您的视图和索引也需要刷新。

                    【讨论】:

                    • 提交时刷新物化视图将阻止您在表更改时手动刷新,但我不确定这是给定问题中的一个选项(尽管没有特别排除)。
                    • 是的。不知道 OP 是否可以在数据库中执行任何操作,但这使他可以选择不使用原始模式。 OP:如果您执行提交时刷新,请确保您没有频繁写入这些表。
                    【解决方案13】:

                    要求第三方为其连接列编制索引,因为他们一开始就应该这样做!没有索引,Oracle 除了蛮力之外别无他法。

                    【讨论】:

                    • 不幸的是,这不是一个选项。我的边缘案例要求不会影响它们的日常运作方式。
                    【解决方案14】:

                    首先我将查询重写为 ANSI 标准:

                    SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
                    FROM items a
                    INNER JOIN itempages b ON b.ItemNum = a.ItemNum
                    INNER JOIN keygroupdata c ON c.ItemNum = b.ItemNum
                    WHERE a.ItemType IN (112,115,189,241)
                    ORDER BY a.DateStored DESC
                    

                    这使得阅读和理解正在发生的事情变得更容易。它还可以帮助您不犯可能导致真正大问题的错误(即交叉连接)。然后我会得到解释计划,看看 DBMS 对那个查询做了什么。它是否试图使用一些索引?是否正确加入表格?

                    然后我会查看正在使用的表,看看是否有任何已经存在的索引可以用来加快查询速度。最后,正如其他人所建议的那样,我将删除 Order By 子句并在代码中执行此操作。

                    【讨论】:

                    • 数据库内核开发人员花费大量时间优化诸如排序技术之类的常见组件不是很明显吗?我觉得不太可能值得为此付出努力
                    • @David Aldridge:我曾经在 SQL Server 7 上完成一个需要 90 秒才能完成的查询。它最初是在 SQL Server 6.5 上编写的,它不支持 ANSI SQL 92 连接语法。作为我努力减少执行时间的一部分,我按照 Rob 的建议做了。然后查询需要 3 秒才能执行。 YMMV。
                    • 很高兴知道 :) 但这是一个真正的暗中刺伤,应该没有区别(当然这是 Oracle)。如果存在差异,则应体现在执行计划差异中。
                    • 我保证查询中的 ORDER BY 会比代码中的排序更快。
                    【解决方案15】:

                    如果没有索引,该查询只会随着表大小的增加而变得更糟。 话虽如此,请尝试删除 order by 子句并在客户端进行排序。

                    【讨论】:

                    • 数据库内核开发人员花费大量时间优化诸如排序技术之类的常见组件不是很明显吗?我觉得这样做不太值得。
                    • 我确信数据库排序已经过优化,这不是问题。他问他如何提高查询性能,最明显的事情就是删除排序。我已经对负载很重的数据库进行了一些真正的改进,从而对客户端进行了排序。
                    【解决方案16】:

                    删除 ORDER BY

                    在将行拉回应用程序后执行排序。

                    【讨论】:

                    • 数据库内核开发人员花费大量时间优化诸如排序技术之类的常见组件不是很明显吗?我觉得这样做不太值得。
                    • 为什么是这样。如果数据库被淹没,就像我假设的那样,那么将数据拉回然后对其进行排序可能会有所帮助。在他的特殊情况下,这需要一些实验来确定这是否是一种有效的方法,但他是在征求意见。那是一个想法。
                    • 好吧,无论如何更新你的回复说,“如果数据库被淹没......”。然后我们可以谈谈“swamped”是什么意思,以及在这种情况下删除 ORDER BY 是否也会将优化器切换到对执行计划产生不利影响的 FIRST_ROWS 模式等。
                    • 这是戴夫的表现规则。如果问题的成本太高而无法提出,请将其改写为更简单的问题。
                    猜你喜欢
                    • 1970-01-01
                    • 2010-11-03
                    • 2018-11-27
                    • 2013-01-16
                    • 2016-10-30
                    • 2021-10-28
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多