Mr.*_*ama 5 sql oracle performance oracle11g
当谈到v$sql_plan_monitor
Oracle 11.2 上的表时,我遇到了一些奇怪的现象。
我有两张大小合适的桌子。一个有大约 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 | |
--------------------------------------------------------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
让我有些悲伤的Rows
数字是这HASH JOIN OUTER
一步的价值。
Oracle 估计它将输出大约 234k 行,这是一个相对较小的数量。我知道一个事实,在过滤*结果后,查询将返回大约 50k 行,因为它之前使用相同的数据运行以进行测试。
*:实际查询本身是一个反连接,使用 aLEFT JOIN
和 aWHERE
来过滤 NULL 记录。
但是,一旦查询运行,我会sql_id
在v$sql_plan_monitor
表中检查它:
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
Run Code Online (Sandbox Code Playgroud)
请注意,查询仍在进行中,因此actual_krows
值正在增长。
所以我的问题是:
为什么估计是错误的?
因为从理论上讲,不可能预测一个程序是否会完成,更不用说预测它需要多长时间了。而且,实际上,估计很困难,Oracle 只有时间来满足;Oracle 不知道查询是每天提交一次还是每秒提交一千次,并且无法花费大量时间进行决定。
我们如何改进估算?
它可能有助于查看整个查询并了解有关表结构和数据分布的一些信息。这是很多信息,但不能保证会有帮助。相反,这里有一些可能对调整基数有用的方法。根据您的查询、会话、环境等,并非所有这些都有帮助。
NVL
表达式有时用OR
.OPT_ESTIMATE
和 提示CARDINALITY
可以帮助弥补错误的估计。 OPT_ESTIMATE
是 SQL 配置文件使用的内容,并且是表达“嘿,将基数增加 1000%”的好方法。 CARDINALITY
是表达“整个查询将返回 X 行”的简单方法。但这些提示很难使用。我们是否需要修正估算?
关心基数是明智的。错误的基数估计会导致许多性能问题。但在许多情况下,基数可能会出现几个数量级的错误,但并不重要。
我没有看到执行计划有任何明显的问题。两个大表以正确的方式访问(如果将使用大多数行,则全表扫描更好),连接方法很好(散列连接对于许多行来说是最好的),连接顺序很好(大表是散列的) (即第一个表),探测更大的表(即第二个表)),并且并行性良好(每个步骤都使用并行性,没有巨大行源的广播等)。
如果这个执行计划就是整个故事,我认为它是成功的。
有时,5 个数量级的偏差并不重要,尤其是当错误接近执行计划末尾时。234K 是一个足够大的数字,可以阻止很多错误,例如错误的交叉连接。
但是,如果这只是较大查询或视图的一部分,则生成的基数可能会影响其他执行计划。