【问题标题】:How to utilize TABLE ACCESS BY INDEX ROWID如何使用 TABLE ACCESS BY INDEX ROWID
【发布时间】:2013-06-30 19:02:45
【问题描述】:

我对使用全表扫描的查询有疑问。

当这个查询在我们的 UAT 环境中运行时,它使用一个 TABLE ACCESS BY INDEX ROWID,但是在 prod 它使用全表扫描。 UAT 比 PROD 运行得更好。

我们在 prod 和 uat 中有相同的表和索引结构。

我已经尝试重建和重新创建索引,但使用了相同的解释计划。

表和索引的静态数据也更新了。

你能帮我让这个查询使用 INDEX 访问而不是 FUll 表访问吗?

请看下面我们的prod和uat的解释计划。

解释计划产品 ==================================================== ==================== SQL> 解释计划 选择 ASV_ODC_BRANCH.CODE、ASV_ODC_BRANCH.DESCRIPTION、ASV_ODC_BRANCH.BRSTN、DEB.VIEW_DORMANT.ACCTNO 作为 DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME 作为 DORMANT_NAME,DEB.VIEW_DORMANT.OPID_ENTRY,DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH、DEB.VIEW_DORMANT、DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), '29', SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) 和 DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; 解释。 计划表输出 |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)| | 0 |选择声明 | | 3 | 489 | 3601 (2)| | 1 |排序方式 | | 3 | 489 | 3601 (2)| | 2 |哈希连接 | | 3 | 489 | 3600 (2)| | 3 |合并加入笛卡尔| | 1 | 90 | 3595 (2)| | 4 |嵌套循环 | | 1 | 66 | 3592 (2)| | 5 | **表访问已满** |帐户 | 1 | 56 | 3590 (2)| | 6 |按索引 ROWID 访问表|扩展1 | 1 | 10 | 2 (0)| | 7 |索引唯一扫描 | PKEXT10 | 1 | | 1 (0)| | 8 |缓冲区排序 | | 1 | 24 | 3593 (2)| | 9 |按索引 ROWID 访问表|客户 | 1 | 24 | 3 (0)| | 10 |索引范围扫描 | UXCUST1 | 1 | | 2 (0)| | 11 |表访问完全 | ASV_ODC_BRANCH |第334章24382 | 5 (0)| **解释计划 UAT** ==================================================== ===================================== SQL> 解释计划 选择 ASV_ODC_BRANCH.CODE、ASV_ODC_BRANCH.DESCRIPTION、ASV_ODC_BRANCH.BRSTN、DEB.VIEW_DORMANT.ACCTNO 作为 DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME 作为 DORMANT_NAME,DEB.VIEW_DORMANT.OPID_ENTRY,DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH、DEB.VIEW_DORMANT、DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), '29', SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) 和 DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; 解释。 SQL> / PLAN_TABLE_OUTPUT |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)| | 0 |选择声明 | | 5 | 5930 | 19 (11)| | 1 |排序方式 | | 5 | 5930 | 19 (11)| | 2 |哈希连接 | | 5 | 5930 | 18(6)| | 3 |合并加入笛卡尔| | 2 | 2220 | 12 (0)| | 4 |嵌套循环 | | 1 | 1085 | 9 (0)| | 5 | **按索引 ROWID 访问表**|帐户 | 1 | 57 | 7 (0)| | 6 |索引跳过扫描 | UXACCT2 | 1 | | 6 (0)| | 7 |按索引 ROWID 访问表|扩展1 | 1 | 1028 | 2 (0)| | 8 |索引唯一扫描 | PKEXT10 | 1 | | 1 (0)| | 9 |缓冲区排序 | | 1 | 25 | 10 (0)| | 10 |按索引 ROWID 访问表|客户 | 1 | 25 | 3 (0)| | 11 |索引范围扫描 | UXCUST1 | 1 | | 2 (0)| | 12 |表访问完全 | ASV_ODC_BRANCH | 336 | 25536 | 5 (0)|

【问题讨论】:

  • 一个数据库如何使用全表扫描而另一个没有,这是个问题,我们该如何改进它

标签: performance oracle indexing


【解决方案1】:

区别在于

解释计划产品

|   5 |      **TABLE ACCESS FULL**      | ACCOUNT        |     1 |    56 |  3590   (2)|

解释计划 UAT

|   5 |  **TABLE ACCESS BY INDEX ROWID**| ACCOUNT        |     1 |    57 |     7   (0)|
|   6 |       INDEX SKIP SCAN           | UXACCT2        |     1 |       |     6   (0)|    

它是如何工作的?

跳过扫描不是使用语句中的谓词来限制搜索路径,而是通过在索引中探测前缀列的不同值来启动的。然后将这些不同的值中的每一个用作常规索引搜索的起点。结果是对单个索引的多次单独搜索,当它们组合时,消除了前缀列的影响。本质上,索引是从第二层往下搜索的。

优化器使用统计信息来确定跳过扫描是否比全表扫描更有效。

优化器认为他比 FTS 更有优势,因为

  1. 它减少了支持一系列查询所需的索引数量。这通过减少索引维护和减少与多个索引相关的空间浪费来提高性能。
  2. 前缀列在查询中应该是最具区分性和最广泛使用的。这两个条件并不总是齐头并进,这使得决策变得困难。在这些情况下,跳过扫描可以减少做出“错误”决定的影响。

你可以考虑以下

  1. 检查整个环境的优化器模式。
  2. 收集查询中使用的所有表的统计信息。例如,如果一张表在创建后没有被分析过,并且如果它在高水位线下的块少于 DB_FILE_MULTIBLOCK_READ_COUNT,那么优化器会认为该表很小并使用全表扫描。查看 ALL_TABLES 表中的 LAST_ANALYZED 和 BLOCKS 列以检查统计信息。
  3. 尽管您的环境相似且代码相同,但优化器将即时检查并选择最佳可用方法。你的 UAT 也使用相同的数据设置。由于它是 UAT(在大多数公司中几乎是预生产),因此它的规模应该是最接近生产的。

【讨论】:

  • +1 用于收集统计数据。这看起来像是一个无聊的缺失或陈旧统计问题,因为执行计划的基数估计如此之低。
【解决方案2】:

据我所知,表DEB.VIEW_DORMANT 是表ACCOUNTEXTENSION1 的视图,您想使用前者的索引UXACCT2。我猜这个请求中的a hint 应该允许你做你想做的事,比如:

SELECT /*+ INDEX(D UXACCT2) */ ASV_ODC_BRANCH.CODE,
...
FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT D, DEB.CUSTOMER
...

PS:如果这是您管理的查询(不是由任何高级软件生成的),我建议您像我一样为您的表使用别名,这会使查询更具可读性...

【讨论】:

  • 您好伊曼纽尔,感谢您的回复。不幸的是,即使在对象上,我也无法编辑脚本上的任何内容。这是我们拥有的外包软件。
  • 也许 SQL 计划管理会有所帮助:docs.oracle.com/cd/E11882_01/server.112/e16638/…
【解决方案3】:

我可以帮助您将其设置为 INDEX-ACCESS 而不是 FTS 吗?大概……

你真的想要吗? - 可能不是

为什么数据库的行为不同?因为您对不同的数据进行操作 - 正如您的示例所示,查询返回不同数量的行,所以我什至不必询问您的生产数据和测试数据是否相同。如果您有不同的数据(索引列中的不同数量或不同值),您的 Database-Stats 将有所不同,您的索引看起来会有所不同,因此优化器将采用不同的查询计划!

你应该怎么做?确保所有索引都是最新的,分区布置合理,所有数据库统计信息都是最新的,并且没有奇怪的调整设置(查询计划,环境设置......)然后优化器在大多数情况下会找到最好的计划 - 在许多情况下,全表扫描是更快的选择。

但是如果你测量时间并且优化器明显走错了路,虽然它有准确的表统计,你应该向 oracle 提交错误报告。

如果您有其他原因希望优化器进行索引访问:

如果您无法像 Emmanuel 提供的那样输入 Optimizer-HINT,您可以尝试 Profiles 或 Baselines,它们提供了很好的调整可能性。您可以使用不同的 WHERE-Predicates 编写自己的语句,直到您获得具有索引​​访问权限的计划并将其用作 SQL 配置文件并将此配置文件链接到原始语句,然后该语句将使用相同的查询计划。

【讨论】:

    猜你喜欢
    • 2020-04-23
    • 2014-02-23
    • 2014-08-03
    • 2020-01-12
    • 2021-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-15
    相关资源
    最近更新 更多