我有一个Oracle 10.2.0.3数据库,以及这样的查询:
select count(a.id)
from LARGE_PARTITIONED_TABLE a
join SMALL_NONPARTITIONED_TABLE b on a.key1 = b.key1 and a.key2 = b.key2
where b.id = 1000
表LARGE_PARTITIONED_TABLE(a)有大约500万行,并由查询中不存在的列分区。表SMALL_NONPARTITIONED_TABLE(b)未分区,并保存约10000行。
统计数据是最新的,并且表a的key1和key2列中有高度平衡的直方图。
表a在列key1,key2,key3,key4和key5上有一个主键和一个全局的,未分区的唯一索引。
查询的解释计划会显示以下结果:
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 31 | 4 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 31 | | |
| 2 | NESTED LOOPS | | 406 | 12586 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN| INDEX_ON_TABLE_B | 1 | 19 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN| PRIMARY_KEY_INDEX_OF_TABLE_A | 406 | 4872 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("b"."id"=1000)
4 - access("a"."key1"="b"."key1" and
"a"."key2"="b"."key2")
因此,对步骤4估计的行(基数) 406 。
现在, tkprof 跟踪显示以下内容:
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=51 pr=9 pw=0 time=74674 us)
7366 NESTED LOOPS (cr=51 pr=9 pw=0 time=824941 us)
1 INDEX RANGE SCAN INDEX_ON_TABLE_B (cr=2 pr=0 pw=0 time=36 us)(object id 111111)
7366 INDEX RANGE SCAN PRIMARY_KEY_INDEX_OF_TABLE_A (cr=49 pr=9 pw=0 time=810173 us)(object id 222222)
所以现实中的基数是 7366 ,而不是406!
我的问题是:在这种情况下,Oracle从哪里获得406的估计基数,如何提高其准确性,以便估算更多根据查询执行过程中发生的事情?
更新:以下是我在查询中运行的10053跟踪的片段。
NL Join
Outer table: Card: 1.00 Cost: 2.00 Resp: 2.00 Degree: 1 Bytes: 19
Inner table: LARGE_PARTITIONED_TABLE Alias: a
...
Access Path: index (IndexOnly)
Index: PRIMARY_KEY_INDEX_OF_TABLE_A
resc_io: 2.00 resc_cpu: 27093
ix_sel: 1.3263e-005 ix_sel_with_filters: 1.3263e-005
NL Join (ordered): Cost: 4.00 Resp: 4.00 Degree: 1
Cost_io: 4.00 Cost_cpu: 41536
Resp_io: 4.00 Resp_cpu: 41536
****** trying bitmap/domain indexes ******
Best NL cost: 4.00
resc: 4.00 resc_io: 4.00 resc_cpu: 41536
resp: 4.00 resp_io: 4.00 resp_cpu: 41536
Using concatenated index cardinality for table SMALL_NONPARTITIONED_TABLE
Revised join sel: 8.2891-e005 = 8.4475e-005 * (1/12064.00) * (1/8.4475e-005)
Join Card: 405.95 = outer (1.00) * inner (4897354.00) * sel (8.2891-e005)
Join Card - Rounded: 406 Computed: 405.95
这就是价值406的来源。就像亚当回答的那样,加入基数是join selectivity * filter cardinality (a) * filter cardinality (b)
,可以在上面跟踪引用的倒数第二行看到。
我不明白的是Revised join sel
行。 1/12064是用于从表b中查找行的索引的选择性(表上的12064行,并且基于唯一ID进行选择)。那么呢?
基数似乎是通过计算得出的 乘以过滤基数 表b (4897354)with 表a的选择性(1/12064)。 为什么?什么 做选择性 表a与多少有关 预计将从中找到行 表b,当a - > b加入不是基于 a.id?
号码在哪里 8.4475e-005 来自(它不会出现在整个其他任何地方 跟踪)?不是它影响了 输出,但我还是想知道。
我知道优化器可能在这里选择了正确的路径。但仍然基数被误算 - 并且这可能对从该点开始选择的执行路径产生重大影响(如我有IRL的情况 - 这个例子是对它的简化) )。
答案 0 :(得分:7)
生成10053跟踪文件将有助于准确显示优化程序在估计基数和选择性方面所做的选择。 Jonathan Lewis的精益求精基于成本的Oracle基础知识是理解优化器如何工作的优秀资源,而且我的打印范围是8i到10.1。
从那项工作:
Join Selectivity = ((num_rows(t1) - num_nulls(t1.c1)) / num_rows(t1))
* ((num_rows(t2) - num_nulls(t2.c2)) / num_rows(t2))
/ greater (num_distinct(t1.c1), num_distinct(t2.c2))
Join Cardinality = Join Selectivity
* filtered_cardinality (t1)
* filtered_cardinality (t2)
但是,因为我们有一个多列连接,所以Join Selectivity不在表级别,它是每列上连接选择性的乘积(交集)。假设游戏中没有空值:
Join Selectivity = Join Selectivity (key1) * Join Selectivity (key2)
Join Selectivity (key1) = ((5,000,000 - 0) / 5,000,000)
* ((10,000 - 0)) / 10,000)
/ max (116, ?) -- distinct key1 values in B
= 1 / max(116, distinct_key1_values_in_B)
Join Selectivity (key2) = ((5,000,000 - 0) / 5,000,000)
* ((10,000 - 0)) / 10,000)
/ max (650, ?) -- distinct key2 values in B
= 1 / max(650, distinct_key2_values in B)
Join Cardinality = JS(key1) * JS(key2)
* Filter_cardinality(a) * Filter_cardinality(b)
我们知道A上没有过滤器,因此表格过滤器基数是行数。我们从B中选择键值,因此表的过滤基数为1.
现在估算估计加入基数的最佳情况是
Join Cardinality = 1/116 * 1/650 * 5,000,000 * 1
=~ 67
向后工作可能更容易。根据我们所知,您的估计基数为406,导致加入选择性为406 / 5,000,000,即大约1/12315。这恰好是非常接近1 /(116 ^ 2),这是优化器中的一个健全性检查,以防止它在多列连接上找到过于激进的基数。
对于TL; DR众人:
答案 1 :(得分:2)
基数估计将基于a.key1和a.key2的选择性的乘积,其中(至少10g)将基于列中记录的这两列的不同值的数量统计
对于一个5M行的表,基数估计值406与7366没有显着差异。您必须问自己的问题是,这里的“不准确”估计会导致问题吗?
如果能够通过为此获得解释计划来生成完全准确的估算,您可以查看Oracle将选择的计划:
select /*+CARDINALITY(a 7366)*/ count(a.id)
from LARGE_PARTITIONED_TABLE a
join SMALL_NONPARTITIONED_TABLE b on a.key1 = b.key1 and a.key2 = b.key2
where b.id = 1000;
如果这提出相同的计划,那么Oracle正在计算的估计已经足够了。
答案 2 :(得分:2)
您可能有兴趣阅读Wolfgang Breitling撰写的这篇优秀论文,其中有很多关于CBO计算的信息:http://www.centrexcc.com/A%20Look%20under%20the%20Hood%20of%20CBO%20-%20the%2010053%20Event.pdf。
正如那里所解释的,因为你有直方图,这些列的滤波因子计算不使用不同值(NDV)的数量,而是使用以某种方式从直方图得到的密度。
对于a.key1和a.key2,USER_TAB_COLUMNS中的DENSITY值是多少?
通常,像这样的情况下的问题是Oracle不会收集有关列对的统计信息,并假设它们的组合过滤因子将是其各个因素的乘积。如果两列的值之间存在任何相关性,则会产生低估计值。
如果这会导致严重的性能问题,我想您可以在这些列的函数上创建基于函数的索引,并使用它来执行查找。然后Oracle将收集有关该指数的统计数据,并可能产生更好的估计值。