【问题标题】:Oracle subquery performanceOracle 子查询性能
【发布时间】:2015-01-17 06:45:43
【问题描述】:

所以我有这个巨大的表 SS(someID, someDate, ...)。我需要将此表的一个子集连接到另一个表。子集由以下方式确定:select * from SS where someID in(select someID from SS where someDate is between date1 and date2)。

在 Oracle XA 数据服务器上并行运行时,执行耗时较长,需要占用 TEMP 空间,尽管 Oracle 可以在 SS 表上实现 99% 的单元卸载效率,但子集查询仍会带回大量与其他表连接时将数据发送到数据库服务器。

有什么办法可以提高效率吗?例如 Oracle 不必发回尽可能多的数据并利用更多的单元卸载效率?

下面是查询计划

PLAN_TABLE_OUTPUT
Plan hash value: 3198983388

---------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name           | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                |  1044K|   589M| 46101   (1)| 00:01:33 |        |      |            |
|   1 |  PX COORDINATOR                         |                |       |       |            |          |        |      |            |
|   2 |   PX SEND QC (RANDOM)                   | :TQ10003       |  1044K|   589M| 46101   (1)| 00:01:33 |  Q1,03 | P->S | QC (RAND)  |
|*  3 |    HASH JOIN BUFFERED                   |                |  1044K|   589M| 46101   (1)| 00:01:33 |  Q1,03 | PCWP |            |
|   4 |     PX RECEIVE                          |                |       |       |            |          |  Q1,03 | PCWP |            |
|   5 |      PX SEND HASH                       | :TQ10001       |       |       |            |          |  Q1,01 | P->P | HASH       |
|   6 |       NESTED LOOPS                      |                |       |       |            |          |  Q1,01 | PCWP |            |
|   7 |        NESTED LOOPS                     |                |   523K|   135M| 38264   (1)| 00:01:17 |  Q1,01 | PCWP |            |
|   8 |         SORT UNIQUE                     |                | 29402 |   401K| 13751   (1)| 00:00:28 |  Q1,01 | PCWP |            |
|   9 |          PX RECEIVE                     |                | 29402 |   401K| 13751   (1)| 00:00:28 |  Q1,01 | PCWP |            |
|  10 |           PX SEND HASH                  | :TQ10000       | 29402 |   401K| 13751   (1)| 00:00:28 |  Q1,00 | P->P | HASH       |
|  11 |            PX BLOCK ITERATOR            |                | 29402 |   401K| 13751   (1)| 00:00:28 |  Q1,00 | PCWC |            |
|* 12 |             INDEX STORAGE FAST FULL SCAN| SUPERSET_IDX1  | 29402 |   401K| 13751   (1)| 00:00:28 |  Q1,00 | PCWP |            |
|* 13 |         INDEX RANGE SCAN                | XU_SUPERSET_01 |    18 |       |     1   (0)| 00:00:01 |  Q1,01 | PCWP |            |
|  14 |        TABLE ACCESS BY INDEX ROWID      | SUPERSET       |    18 |  4644 |     2   (0)| 00:00:01 |  Q1,01 | PCWP |            |
|  15 |     PX RECEIVE                          |                |  2886K|   880M|  7834   (2)| 00:00:16 |  Q1,03 | PCWP |            |
|  16 |      PX SEND HASH                       | :TQ10002       |  2886K|   880M|  7834   (2)| 00:00:16 |  Q1,02 | P->P | HASH       |
|  17 |       PX BLOCK ITERATOR                 |                |  2886K|   880M|  7834   (2)| 00:00:16 |  Q1,02 | PCWC |            |
|  18 |        TABLE ACCESS STORAGE FULL        | POL_DTL        |  2886K|   880M|  7834   (2)| 00:00:16 |  Q1,02 | PCWP |            |
---------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access(SS.POL_ID=PD.POL_ID)
  12 - storage(IMPT_DT<=TO_DATE(' 2014-11-20 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND IMPT_DT>=TO_DATE(' 2014-10-28 
              00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
       filter(IMPT_DT<=TO_DATE(' 2014-11-20 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND IMPT_DT>=TO_DATE(' 2014-10-28 
              00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
  13 - access(SS.POL_ID=POL_ID)

Note
-----
   - Degree of Parallelism is 4 because of session

【问题讨论】:

  • select * from SS where someID in (select someID from SS where someDate is between date1 and date2). 这真的可以称为self join吗?或者它是一个相关的子查询?我不知道这是否会改变优化器的某些东西...无论如何,如果没有查询计划,很难回答这样的问题。
  • 我认为select * from SS where someDate is between date1 and date2 返回的结果与您的查询相同,所以我不太明白这一点
  • 我已经添加了执行统计和计划。如您所见,有很多活动......
  • 请运行命令:EXPLAIN PLAN FOR your_query_goes_here ;,然后运行SELECT * FROM table(DBMS_XPLAN.DISPLAY),最后请将最后一个查询的结果复制到剪贴板(以文本格式),并将其粘贴到答案中。图像很漂亮,但很难阅读。谢谢。
  • 我不确定如何从计划中保留表格格式。每次我复制/粘贴到这里它都会丢失所有格式:(如果你右键单击图像,然后在新选项卡中打开,它应该显示得很清楚。我从 Oracle Enterprise Manager 拍摄了这张快照

标签: oracle performance join oracle11g subquery


【解决方案1】:

您可能无法改进此查询。执行计划看起来不错:

  1. 好对象 索引似乎很适合查询,尽管没有完整的定义很难判断。
  2. 良好的基数 估计行和实际行接近。这强烈暗示优化器做得很好并且正在选择一个接近最优的计划。如果它可以正确估计行数,它将对访问路径、连接方法、连接顺序等做出明智的决定。即使时间估计很接近,这也是很少见的。看起来有很好的表和系统统计信息。
  3. 单元卸载 storage 谓词和活动报告Cell offloading 暗示单元卸载正在按预期工作,至少一次。
  4. 并行度 大型对象已经在并行处理。我没有看到任何明显的并行问题。

这里有一些改进的想法,但不要指望会有很大的改进:

  1. 全表扫描 强制全表扫描,而不是索引范围扫描,提示类似--+ no_index(superset XU_SUPERSET_01)。使用多块读取(用于完整扫描)和单元卸载(用于完整扫描的直接路径读取,不用于使用缓冲区缓存的索引范围扫描),读取所有数据的完整表扫描可能比读取所有数据更有效读取较少数据的索引范围扫描。
  2. 覆盖索引 如果全表扫描不起作用,请创建一个包含所有返回和查询列的索引的精简版表。这获得了全扫描(多块 IO、单元卸载)的好处,但比全表小。
  3. 更大的 DOP 并行度 (DOP) 没有神奇的数字。但根据我的经验,DOP 最佳点几乎总是大于 4。这可能会提高性能,但会占用更多资源。
  4. 重写查询? 重写查询可以启用智能扫描来处理存储单元中的连接。尝试改变

    select * from SS where someID in 
      (select someID from SS where someDate is between date1 and date2)
    

    到

    select distinct ss1.*
    from ss ss1
    join ss ss2
        on ss1.someID = ss2.someID
         and ss2.someDate is between date1 and date2
    

    这个新版本做了额外的工作。连接返回的行数超过了必要的行数,然后需要区分它们。如果这意味着连接可以在存储单元中发生,那么额外的工作可能是值得的。我找不到确切可以卸载哪种处理的重要来源,但至少可以卸载某些类型的连接。

【讨论】:

  • 是的,我不认为它可以进一步改进。谢谢
猜你喜欢
  • 1970-01-01
  • 2020-11-03
  • 1970-01-01
  • 2013-01-16
  • 2011-03-09
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多