Oracle 12.2:优化器在末尾添加 FOR UPDATE OF ... 块时更改 SELECT 查询的解释计划

Posted

技术标签:

【中文标题】Oracle 12.2:优化器在末尾添加 FOR UPDATE OF ... 块时更改 SELECT 查询的解释计划【英文标题】:Oracle 12.2: Optimizer changes the explain plan of a SELECT query when adding an FOR UPDATE OF ... block at the end 【发布时间】:2021-04-14 09:02:08 【问题描述】:

我有一个(相当复杂的)200 多行 SELECT 查询,由一个运行良好的计费应用程序发起。 当应用程序执行 THE SAME 查询并在末尾添加 FOR UPDATE OF .. 块时,优化器会大幅更改执行计划并且性能会变差。

实际上问题出在优化器放弃“VIEW PUSHED PREDICATE”方法导致性能不佳的特定区域。我尝试使用 PUSH_PRED 提示强制执行“PUSHED PREDICATE”方法,但没有任何运气,这很可能是由于查询的复杂性(在所有 SELECT 、 FROM 和 WHERE 块中都有子查询) 除了从头开始重写查询以达到可接受的性能,然后为优化器应用 SQL 配置文件之外,还有关于如何克服这种情况的任何快速提示。 您是否遇到过类似的问题?

感谢和问候 迪米特里斯 G

【问题讨论】:

【参考方案1】:

您需要为我们发布更多详细信息,以帮助您了解您的情况下计划发生变化的原因以及如何避免这种情况,但添加 for update 当然可以改变计划。

这是一个简单的例子

查询连接t1t2 的子查询。 t2 的所有引用列都在索引中。因此,数据库能够从索引中获取t2 所需的所有值,而优化器只读取索引,根本不访问表t2

create table t1 as 
  select level c1, 'xxxxx' c2 
  from   dual
  connect by level <= 100;
  
create table t2 as 
  select c1, c2, c3, 'yyyy' c4 
  from   t1
  cross join ( 
    select level c3
    from   dual
    connect by level <= 10
  );

create index i2 on t2 ( c1, c4 );

set serveroutput off

select * from t1
join ( 
  select c1, c4
  from   t2
) s 
on    t1.c1 = s.c1
where t1.c1 = 1
and   s.c4 = 'yyyy';

select * 
from   table(dbms_xplan.display_cursor( format => 'BASIC LAST'));

-------------------------------------                                       
| Id  | Operation            | Name |                                       
-------------------------------------                                       
|   0 | SELECT STATEMENT     |      |                                       
|   1 |  MERGE JOIN CARTESIAN|      |                                       
|   2 |   TABLE ACCESS FULL  | T1   |                                       
|   3 |   BUFFER SORT        |      |                                       
|   4 |    INDEX RANGE SCAN  | I2   |                                       
-------------------------------------  

但是添加一个包含来自t2 的值的for update 子句,并且数据库现在必须 访问此表以锁定行。所以之前的计划不再有效,改为全扫描t2

select * from t1
join ( 
  select c1, c4
  from   t2
) s 
on    t1.c1 = s.c1
where t1.c1 = 1
and   s.c4 = 'yyyy'
for   update;

select * 
from   table(dbms_xplan.display_cursor( format => 'BASIC LAST'));

-------------------------------------                                       
| Id  | Operation            | Name |                                       
-------------------------------------                                       
|   0 | SELECT STATEMENT     |      |                                       
|   1 |  FOR UPDATE          |      |                                       
|   2 |   BUFFER SORT        |      |                                       
|   3 |    HASH JOIN         |      |                                       
|   4 |     TABLE ACCESS FULL| T1   |                                       
|   5 |     TABLE ACCESS FULL| T2   |                                       
------------------------------------- 

因此,添加 for update 可能会禁用某些访问路径,从而导致您获得不同的计划。

【讨论】:

你说得对!感谢您的回复。 @chris-saxon "for update" 从 11.2.0.2 开始需要父级 "buffer sort" 操作:orasql.org/2013/02/16/… 这会导致成本发生一些变化,它还会将 optimizer_mode 更改为所有行 @SayanMalakshinov 谢谢,不知道。也许您可以发布一个答案,显示除了添加此操作之外的更改计划

以上是关于Oracle 12.2:优化器在末尾添加 FOR UPDATE OF ... 块时更改 SELECT 查询的解释计划的主要内容,如果未能解决你的问题,请参考以下文章

oracle 12.2版本优化情况

分而治之:Oracle 18c 及 12.2 分区新特性的 N 种优化实践

Oracle 12.2报错ORA-15032ORA-15410或ORA-15411解决

Oracle Apps DBA R12.2 Syllabus

Oracle 12.2 - NOPARTITION 特性的替换

Linux系统Oracle 12cR2 RAC集群安装与维护管理(12.2)专题