关于Oracle 11.2上的 v$sql_plan_monitor
表,我遇到了一个奇怪的现象。
我有两个尺寸合适的 table 。一个大约有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估计它将输出大约23.4万行,数量相对较少。我知道一个事实,查询将过滤*结果后返回大约5万行,因为它之前已经运行了相同的数据用于测试。
*:实际查询本身是一个反联接,使用
LEFT JOIN
和WHERE
过滤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
请注意,查询仍在进行中,因此
actual_krows
值正在增长。所以我的问题是:
最佳答案
为什么估算错误?
因为从理论上来说it is impossible to predict if a program will ever finish,更不用说预测需要多长时间了。而且,实际上,估算是困难的,并且Oracle仅有时间使用satisficing; Oracle不知道查询是每天提交一次还是每秒提交一千次,因此无法花很多时间来决定。
我们如何改善估算?
查看整个查询并获得有关表结构和数据分布的一些信息可能会有所帮助。这是很多信息,不能保证会有所帮助。相反,这是一堆可能对调整基数有用的方法。根据您的查询, session ,环境等,并非所有这些都将有所帮助。
NVL
更好地编写一些复杂的OR
表达式。 特定于SQL语句的统计信息。”例如,表统计信息可能意味着只有10%的行会加入,并且配置文件可能会说“将其乘以1000”。
OPT_ESTIMATE
和CARDINALITY
提示可以帮助弥补错误的估计。 OPT_ESTIMATE
是SQL配置文件使用的,并且是说“嘿,将基数提高1000%”的好方法。 CARDINALITY
是说“整个查询将返回X行”的简单方法。但是这些提示很难使用。 我们甚至需要确定估算值吗?
考虑基数是明智的。基数估计错误会导致许多性能问题。但是在许多情况下,基数可能错了几个数量级,并不重要。
我认为执行计划没有任何明显的问题。以正确的方式访问两个大表(如果将使用大多数行,则进行全表扫描会更好),join方法很好(散列联接对于许多行来说最好),联接顺序很好(对大表进行哈希处理) (即第一个表),较大的表(即第二个表)被探测到,并且并行性很好(在每个步骤中使用并行性,没有广播巨大的行源,等等)。
如果说执行计划是全部,那么我会称之为成功。
有时5个数量级的差异并不重要,尤其是当错误接近执行计划的末尾时。 234K足够大,可以阻止很多错误,例如不良的交叉连接。
但是,如果这仅是较大查询或 View 的一部分,则结果基数可能会影响其他执行计划。
关于sql - v $ sql_plan_monitor-JOIN估计非常不准确?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27431455/