为什么优化器计划与实验性查询运行不相关?
Why optimizer plan doesn't correlate with experimental query runs?
假设我们有以下问题:
给定一个 table 一列 'X'
,包含一些随机的行
从 1 到 100 的整数:
CREATE TABLE xtable(x) AS
SELECT ceil(dbms_random.value * 100)
FROM dual
CONNECT BY level <= 1000000;
我们必须删除重复的行,以便所有不同的整数保留在 table。
让我们考虑下面的三个解决方案(平均执行时间和优化器计划)。
我必须补充一点,实验表明:
- 解决方案 1 和 2 是可扩展的,并且随着每个行数量的增加呈线性时间增长(使用 table 最多 1000 万行进行测试)
- 解决方案 3 的时间呈指数增长,大致类似于
3 * exp(0.6 * N)
我们看到对于解决方案 2 优化器计划给出与实验结果无关的期望,
甚至与他们相反:
- 成本和其他值在方案 2 和方案 3 中几乎相同
- 解决方案 1 和 2 的执行时间几乎相同
And in this experiments the presence or absence of gathered statistics for the table
doesn't affect optimizer plans and execution times.
请解释为什么我不能相信情况 2 中的优化程序计划。
是什么导致优化器忽略线性和指数复杂度之间的明显差异?
解决方案:
1.
DELETE xtable WHERE rowid IN (
SELECT ri from (
SELECT rowid AS ri,
row_number() OVER(PARTITION BY x ORDER BY null) AS rn
FROM xtable
)
WHERE rn > 1
)
Exe time: 14 - 16 secs
Plan:
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1000000 | 15000000 | 5119 | 00:00:01 |
| 1 | DELETE | XTABLE | | | | |
| * 2 | HASH JOIN SEMI | | 1000000 | 15000000 | 5119 | 00:00:01 |
| 3 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
| 4 | VIEW | VW_NSO_1 | 1000000 | 12000000 | 2976 | 00:00:01 |
| * 5 | VIEW | | 1000000 | 25000000 | 2976 | 00:00:01 |
| 6 | WINDOW SORT | | 1000000 | 3000000 | 2976 | 00:00:01 |
| 7 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access(ROWID="RI")
* 5 - filter("RN">1)
2.
DELETE xtable WHERE (x, rowid) NOT IN (SELECT x, min(rowid) FROM xtable GROUP BY x)
Exe time: 15 - 17 secs
Plan:
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 50000 | 150000 | 278162850 | 03:01:06 |
| 1 | DELETE | XTABLE | | | | |
| 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 281 | 00:00:01 |
| 4 | FILTER | | | | | |
| 5 | SORT GROUP BY NOSORT | | 1000000 | 3000000 | 280 | 00:00:01 |
| 6 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 5 - access(INTERNAL_FUNCTION("X")=INTERNAL_FUNCTION("X") AND INTERNAL_FUNCTION(ROWID)=INTERNAL_FUNCTION("MIN(ROWID)"))
* 5 - filter(INTERNAL_FUNCTION(ROWID)=INTERNAL_FUNCTION("MIN(ROWID)") AND INTERNAL_FUNCTION("X")=INTERNAL_FUNCTION("X"))
3.
DELETE xtable a WHERE EXISTS(select 1 FROM xtable b WHERE a.x = b.x AND a.rowid < b.rowid)
Exe time: 970 - 990 sec
Plan:
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 50000 | 300000 | 278208956 | 03:01:08 |
| 1 | DELETE | XTABLE | | | | |
| * 2 | FILTER | | | | | |
| 3 | NESTED LOOPS SEMI | | 50000 | 300000 | 278208956 | 03:01:08 |
| 4 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
| * 5 | TABLE ACCESS BY ROWID RANGE | XTABLE | 50000 | 150000 | 278 | 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - filter(:VAR2=:VAR1)
* 5 - access("B".ROWID>"A".ROWID)
计划于 Oracle 12.1.0.2.0
获得
无法重现第二个计划。来了:
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | | | | 3648 (100)| |
| 1 | DELETE | XTABLE | | | | | |
| 2 | MERGE JOIN ANTI NA | | 999K| 26M| | 3648 (5)| 00:00:01 |
| 3 | SORT JOIN | | 1000K| 2929K| 22M| 3147 (3)| 00:00:01 |
| 4 | TABLE ACCESS FULL | XTABLE | 1000K| 2929K| | 434 (3)| 00:00:01 |
|* 5 | SORT UNIQUE | | 100 | 2500 | | 500 (16)| 00:00:01 |
| 6 | VIEW | VW_NSO_1 | 100 | 2500 | | 499 (16)| 00:00:01 |
| 7 | SORT GROUP BY | | 100 | 300 | | 499 (16)| 00:00:01 |
| 8 | TABLE ACCESS FULL| XTABLE | 1000K| 2929K| | 434 (3)| 00:00:01 |
-------------------------------------------------------------------------------------------
Please, explain why I can't trust the optimizer plan in case 2.
你永远不应该相信优化器。 CBO是95%正确,但你不知道哪5%是错误的。
典型问题是使用EXPLAIN PLAN
显示的执行计划与执行使用的计划不一致。 (你不说你是怎么拿到plan的)
长期使用 DBMS_SQLTUNE.REPORT_SQL_MONITOR 有疑问 运行 查询实际计划和有问题的部分。
What causes the optimizer to ignore the obvious difference between linear and exponential complexity?
看到上面忘记了计划的成本比较。在处理 整个 table 时要避免的是 NESTED LOOP
处理。
这正是情况 3 中发生的情况。
| 3 | NESTED LOOPS SEMI | | 50000| 300000 | 278208956 | 03:01:08|
| 4 | TABLE ACCESS FULL |XTABLE | 1000000| 3000000 | 280 | 00:00:01|
| 5 | TABLE ACCESS BY ROWID RANGE |XTABLE | 50000| 150000 | 278 | 00:00:01|
您想查看 SORT 和 HASH JOIN 这是计划 1 显示的内容。
在我看来,计划 2 不会随重复记录的数量变化(简单地尝试 table 每行两次,看看你是否得到与案例 3) 相同的经过时间。
优化器无法估计重复记录的数量,因此防御性地估计了很多,因此成本也很高。
最后但有一点 - 理论说你不应该观察到 线性行为 但最多 O(n * log(n))
.
最后一句话 - 您的测试数据对于删除重复项来说是不现实的。通常你有一个大的 table 和少量的重复。在您的设置中,除 100 条以外的所有记录都是重复的。
删除的成本主导了查找重复项的成本,因此您观察到线性行为。
试试
CREATE TABLE xtable(x) AS
SELECT ceil(dbms_random.value * 100000000)
FROM dual
CONNECT BY level <= 1000000;
select count(*) total, count(*)- count(distinct x) to_be_deleted from xtable;
TOTAL TO_BE_DELETED
---------- -------------
1000000 5083
因此您将删除 0.5% 的记录。现在缩放,你会观察到完全不同的模式。
假设我们有以下问题:
给定一个 table 一列
'X'
,包含一些随机的行 从 1 到 100 的整数:CREATE TABLE xtable(x) AS SELECT ceil(dbms_random.value * 100) FROM dual CONNECT BY level <= 1000000;
我们必须删除重复的行,以便所有不同的整数保留在 table。
让我们考虑下面的三个解决方案(平均执行时间和优化器计划)。
我必须补充一点,实验表明:
- 解决方案 1 和 2 是可扩展的,并且随着每个行数量的增加呈线性时间增长(使用 table 最多 1000 万行进行测试)
- 解决方案 3 的时间呈指数增长,大致类似于
3 * exp(0.6 * N)
我们看到对于解决方案 2 优化器计划给出与实验结果无关的期望, 甚至与他们相反:
- 成本和其他值在方案 2 和方案 3 中几乎相同
- 解决方案 1 和 2 的执行时间几乎相同
And in this experiments the presence or absence of gathered statistics for the table doesn't affect optimizer plans and execution times.
请解释为什么我不能相信情况 2 中的优化程序计划。
是什么导致优化器忽略线性和指数复杂度之间的明显差异?
解决方案:
1.
DELETE xtable WHERE rowid IN (
SELECT ri from (
SELECT rowid AS ri,
row_number() OVER(PARTITION BY x ORDER BY null) AS rn
FROM xtable
)
WHERE rn > 1
)
Exe time: 14 - 16 secs
Plan:
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1000000 | 15000000 | 5119 | 00:00:01 |
| 1 | DELETE | XTABLE | | | | |
| * 2 | HASH JOIN SEMI | | 1000000 | 15000000 | 5119 | 00:00:01 |
| 3 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
| 4 | VIEW | VW_NSO_1 | 1000000 | 12000000 | 2976 | 00:00:01 |
| * 5 | VIEW | | 1000000 | 25000000 | 2976 | 00:00:01 |
| 6 | WINDOW SORT | | 1000000 | 3000000 | 2976 | 00:00:01 |
| 7 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access(ROWID="RI")
* 5 - filter("RN">1)
2.
DELETE xtable WHERE (x, rowid) NOT IN (SELECT x, min(rowid) FROM xtable GROUP BY x)
Exe time: 15 - 17 secs
Plan:
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 50000 | 150000 | 278162850 | 03:01:06 |
| 1 | DELETE | XTABLE | | | | |
| 2 | FILTER | | | | | |
| 3 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 281 | 00:00:01 |
| 4 | FILTER | | | | | |
| 5 | SORT GROUP BY NOSORT | | 1000000 | 3000000 | 280 | 00:00:01 |
| 6 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 5 - access(INTERNAL_FUNCTION("X")=INTERNAL_FUNCTION("X") AND INTERNAL_FUNCTION(ROWID)=INTERNAL_FUNCTION("MIN(ROWID)"))
* 5 - filter(INTERNAL_FUNCTION(ROWID)=INTERNAL_FUNCTION("MIN(ROWID)") AND INTERNAL_FUNCTION("X")=INTERNAL_FUNCTION("X"))
3.
DELETE xtable a WHERE EXISTS(select 1 FROM xtable b WHERE a.x = b.x AND a.rowid < b.rowid)
Exe time: 970 - 990 sec
Plan:
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 50000 | 300000 | 278208956 | 03:01:08 |
| 1 | DELETE | XTABLE | | | | |
| * 2 | FILTER | | | | | |
| 3 | NESTED LOOPS SEMI | | 50000 | 300000 | 278208956 | 03:01:08 |
| 4 | TABLE ACCESS FULL | XTABLE | 1000000 | 3000000 | 280 | 00:00:01 |
| * 5 | TABLE ACCESS BY ROWID RANGE | XTABLE | 50000 | 150000 | 278 | 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - filter(:VAR2=:VAR1)
* 5 - access("B".ROWID>"A".ROWID)
计划于 Oracle 12.1.0.2.0
无法重现第二个计划。来了:
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | | | | 3648 (100)| |
| 1 | DELETE | XTABLE | | | | | |
| 2 | MERGE JOIN ANTI NA | | 999K| 26M| | 3648 (5)| 00:00:01 |
| 3 | SORT JOIN | | 1000K| 2929K| 22M| 3147 (3)| 00:00:01 |
| 4 | TABLE ACCESS FULL | XTABLE | 1000K| 2929K| | 434 (3)| 00:00:01 |
|* 5 | SORT UNIQUE | | 100 | 2500 | | 500 (16)| 00:00:01 |
| 6 | VIEW | VW_NSO_1 | 100 | 2500 | | 499 (16)| 00:00:01 |
| 7 | SORT GROUP BY | | 100 | 300 | | 499 (16)| 00:00:01 |
| 8 | TABLE ACCESS FULL| XTABLE | 1000K| 2929K| | 434 (3)| 00:00:01 |
-------------------------------------------------------------------------------------------
Please, explain why I can't trust the optimizer plan in case 2.
你永远不应该相信优化器。 CBO是95%正确,但你不知道哪5%是错误的。
典型问题是使用EXPLAIN PLAN
显示的执行计划与执行使用的计划不一致。 (你不说你是怎么拿到plan的)
长期使用 DBMS_SQLTUNE.REPORT_SQL_MONITOR 有疑问 运行 查询实际计划和有问题的部分。
What causes the optimizer to ignore the obvious difference between linear and exponential complexity?
看到上面忘记了计划的成本比较。在处理 整个 table 时要避免的是 NESTED LOOP
处理。
这正是情况 3 中发生的情况。
| 3 | NESTED LOOPS SEMI | | 50000| 300000 | 278208956 | 03:01:08|
| 4 | TABLE ACCESS FULL |XTABLE | 1000000| 3000000 | 280 | 00:00:01|
| 5 | TABLE ACCESS BY ROWID RANGE |XTABLE | 50000| 150000 | 278 | 00:00:01|
您想查看 SORT 和 HASH JOIN 这是计划 1 显示的内容。
在我看来,计划 2 不会随重复记录的数量变化(简单地尝试 table 每行两次,看看你是否得到与案例 3) 相同的经过时间。 优化器无法估计重复记录的数量,因此防御性地估计了很多,因此成本也很高。
最后但有一点 - 理论说你不应该观察到 线性行为 但最多 O(n * log(n))
.
最后一句话 - 您的测试数据对于删除重复项来说是不现实的。通常你有一个大的 table 和少量的重复。在您的设置中,除 100 条以外的所有记录都是重复的。
删除的成本主导了查找重复项的成本,因此您观察到线性行为。
试试
CREATE TABLE xtable(x) AS
SELECT ceil(dbms_random.value * 100000000)
FROM dual
CONNECT BY level <= 1000000;
select count(*) total, count(*)- count(distinct x) to_be_deleted from xtable;
TOTAL TO_BE_DELETED
---------- -------------
1000000 5083
因此您将删除 0.5% 的记录。现在缩放,你会观察到完全不同的模式。