【问题标题】:v$sql_plan_monitor - Terribly inaccurate JOIN estimate?v$sql_plan_monitor - JOIN 估计非常不准确?
【发布时间】:2015-02-10 10:40:28
【问题描述】:

当谈到 Oracle 11.2 上的 v$sql_plan_monitor 表时,我遇到了一些奇怪的现象。

我有两张大小合适的桌子。一个有大约 2500 万行,另一个大约有 3500 万行,两者都是约 99% 的唯一性,只有少数几个重复记录。

解释计划如下(表名代替隐私,表在解释计划之前收集了统计信息):

--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                      |       |       | 65611 (100)|          |        |      |            |
|   1 |  SORT AGGREGATE            |                      |     1 |    34 |            |          |        |      |            |
|   2 |   PX COORDINATOR           |                      |       |       |            |          |        |      |            |
|   3 |    PX SEND QC (RANDOM)     | :TQ10002             |     1 |    34 |            |          |  Q1,02 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE         |                      |     1 |    34 |            |          |  Q1,02 | PCWP |            |
|*  5 |      FILTER                |                      |       |       |            |          |  Q1,02 | PCWC |            |
|*  6 |       HASH JOIN OUTER      |                      |   234K|  7770K| 65611   (1)| 00:19:41 |  Q1,02 | PCWP |            |
|   7 |        PX RECEIVE          |                      |    23M|   513M| 26409   (1)| 00:07:56 |  Q1,02 | PCWP |            |
|   8 |         PX SEND HASH       | :TQ10000             |    23M|   513M| 26409   (1)| 00:07:56 |  Q1,00 | P->P | HASH       |
|   9 |          PX BLOCK ITERATOR |                      |    23M|   513M| 26409   (1)| 00:07:56 |  Q1,00 | PCWC |            |
|* 10 |           TABLE ACCESS FULL| PRETTY_BIG_TABLE     |    23M|   513M| 26409   (1)| 00:07:56 |  Q1,00 | PCWP |            |
|  11 |        PX RECEIVE          |                      |    36M|   384M| 39164   (1)| 00:11:45 |  Q1,02 | PCWP |            |
|  12 |         PX SEND HASH       | :TQ10001             |    36M|   384M| 39164   (1)| 00:11:45 |  Q1,01 | P->P | HASH       |
|  13 |          PX BLOCK ITERATOR |                      |    36M|   384M| 39164   (1)| 00:11:45 |  Q1,01 | PCWC |            |
|* 14 |           TABLE ACCESS FULL| EVEN_BIGGER_TABLE    |    36M|   384M| 39164   (1)| 00:11:45 |  Q1,01 | PCWP |            |
--------------------------------------------------------------------------------------------------------------------------------

让我感到悲伤的数字是Rows 的值HASH JOIN OUTER 步骤。
Oracle 估计它将输出大约 234k 行,这是一个相对较小的数量。我知道查询将在过滤*结果后返回大约 50k 行,因为它之前使用相同的数据运行以进行测试。

*:实际查询本身是反连接,使用LEFT JOINWHERE 过滤NULL 记录。

但是,一旦查询运行,我会在v$sql_plan_monitor 表中检查它的sql_id

  1  SELECT
  2      plan_line_id,
  3      plan_operation,
  4      ROUND(MAX(plan_cardinality) / 1000) AS est_krows,
  5      ROUND(SUM(output_rows) / 1000) AS actual_krows
  6  FROM v$sql_plan_monitor
  7  WHERE sql_id = 'sql_id_goes_here'
  8  GROUP BY sql_id, sql_exec_id, sql_exec_start, plan_line_id, plan_operation
  9* ORDER BY sql_exec_id, plan_line_id
SQL> /

PLAN_LINE_ID PLAN_OPERATION                  EST_KROWS ACTUAL_KROWS
------------ ------------------------------ ---------- ------------
           0 SELECT STATEMENT                                     0
           1 SORT                                    0            0
           2 PX COORDINATOR                                       0
           3 PX SEND                                 0            0
           4 SORT                                    0            0
           5 FILTER                                               0
           6 HASH JOIN                             234     23084866
           7 PX RECEIVE                          23402        23168
           8 PX SEND                             23402        23168
           9 PX BLOCK                            23402        23168
          10 TABLE ACCESS                        23402        23168
          11 PX RECEIVE                          36699        17772
          12 PX SEND                             36699        17748
          13 PX BLOCK                            36699        17748
          14 TABLE ACCESS                        36699        17748

请注意,查询仍在进行中,因此actual_krows 的值正在增长。

所以我的问题是:

  1. 为什么 HASH JOIN 的实际行数相差 5 个数量级?
  2. 如何才能获得更准确的估计或更准确地读取实际行输出进度?它似乎只是在 JOIN 上给我带来了糟糕的结果,没有别的。

【问题讨论】:

  • 您的表中有相关​​的列吗?即“如果 COL_A 有“1”,那么 COL_B 总是有“X”。当你将反连接重写为不存在子句时会发生什么?
  • @ibre5041 - 据我所知,表上的所有列都没有关联。至于NOT EXISTS 子句,那不是将计划强制纳入NESTED LOOPs 吗?我的印象是它对较大的表有显着的性能影响。

标签: sql oracle performance oracle11g


【解决方案1】:

为什么估计错误?

因为理论上it is impossible to predict if a program will ever finish,更不用说预测需要多长时间了。而且,实际上,估计是困难的,甲骨文只有satisficing的时间; Oracle 不知道查询是每天提交一次还是每秒提交一千次,因此无法花费大量时间来决定。

我们如何改进估算?

查看整个查询并获得有关表结构和数据分布的一些信息可能会有所帮助。这是很多信息,不能保证它会有所帮助。相反,这里有一些方法可能对调整基数有用。根据您的查询、会话、环境等,并非所有这些都有帮助。

  1. ASSOCIATE STATISTICS 估计声明式代码已经够难的了,Oracle 在使用过程式代码时不会太费劲。如果有自定义函数,默认估计会很差。但是您可以指定自定义选择性来更改估计值。在极少数情况下,可能值得将复杂的 SQL 表达式替换为具有相关统计信息的函数。
  2. 虚假统计 DBMS_STATS.SET_COLUMN_STATS 和其他功能允许您将输入更改为估计算法。但请注意,您对这一查询的修复不会破坏其他具有完全合理估计的查询。
  3. Extended statistics 正如 ibre5041 所提到的,列组或表达式可能难以估计。相反,您可以让 Oracle 收集有关这些组和表达式的统计信息。然后,在查询中使用它们时,估计值可能会好得多。
  4. 重写条件 某些类型的表达式比其他表达式更难估计。如果可能,请尝试重新考虑您的表达方式。例如,一些复杂的NVL 表达式有时可以用OR 更好地编写。
  5. SQL Profiles "SQL 配置文件是一个包含辅助 特定于 SQL 语句的统计信息。”例如,表统计信息可能意味着只有 10% 的行连接,而配置文件可能会说“将其乘以 1000”。
  6. 未记录的提示 OPT_ESTIMATECARDINALITY 提示可以帮助弥补错误估计。 OPT_ESTIMATE 是 SQL 配置文件使用的,并且是说“嘿,将基数增加 1000%”的好方法。 CARDINALITY 是一种简单的说法,即“整个查询将返回 X 行”。但这些提示很难使用。
  7. 动态采样 `/*+ dynamic_sampling(4) */ 这样的提示是告诉优化器“这是一个昂贵的查询,花时间读取现有数据,尝试一下,然后调整数字”。至少,理论是这样的。在实践中,它并不总是很有帮助。
  8. 基数反馈 运行语句两次,如果基数严重错误,Oracle 可能会在第二次修复它。
  9. 自适应查询优化 12c 引入了一项功能,执行计划会偶尔检查行数,并在估计错误时自行修复。这并不能解决根本原因。您还不能使用它。但这听起来很酷,可能是开始考虑升级的好理由。

我们还需要修正估算值吗?

关注基数是明智的。糟糕的基数估计会导致许多性能问题。但在许多情况下,基数可能会出现数量级的错误,这无关紧要。

我没有看到执行计划有任何明显的问题。两个大表访问方式正确(全表扫描最好,如果大部分行会被使用),连接方法好(散列连接最好多行),连接顺序好(大表散列(即第一个表),探测较大的表(即第二个表),并行性好(每一步都使用并行,没有大行源的广播等)。

如果该执行计划是整个故事,我会称之为成功。

有时减少 5 个数量级并不重要,尤其是当错误接近执行计划的末尾时。而且 234K 是一个足够大的数字,可以阻止很多错误,比如错误的交叉连接。

但是,如果这只是较大查询或视图的一部分,则生成的基数可能会影响其他执行计划。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 2018-08-01
    • 2019-05-01
    • 1970-01-01
    相关资源
    最近更新 更多