简单的Oracle UPDATE语句异常糟糕的性能

时间:2013-03-17 16:36:02

标签: oracle performance

每个月我都在我的oracle数据库上做一个简单的更新语句。但是,因为星期一它需要很长时间。该表每月增长5%。现在存储了800万条记录。

声明:

update /*+ parallel(destination_tab, 4) */ destination_tab dest    
   set (full_name, state) =   
       (select /*+ parallel(source_tab, 4) */ dest.name, src.state   
        from source_tab src   
        where src.city = dest.city);

实际上有20个字段需要更新,而不仅仅是两个...但所以看起来更容易描述问题。

解释计划:

-----------------------------------------------------------------------------------------------------                               
| Id  | Operation                    | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |                                   
-----------------------------------------------------------------------------------------------------                               
|   0 | update statement             |                      |  8517K|  3167M|   579M (50)|999:59:59 |                                   
|   1 |  update                      | destination_tab      |       |       |            |          |
|   2 |   PX COORDINATOR             |                      |       |       |            |          |
|   3 |    PX SEND QC (RANDOM)       | :TQ10000             |  8517K|  3167M|  6198   (1)| 00:01:27 |
|   4 |     px block iterator        |                      |  8517K|  3167M|  6198   (1)| 00:01:27 |
|   5 |      table access full       | DESTINATION_TAB      |  8517K|  3167M|  6198   (1)| 00:01:27 |
|   6 |   table access by index rowid| SOURCE_TAB           |     1 |    56 |     1   (0)| 00:00:01 |
|*  7 |    index unique scan         | CITY_PK              |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

有人可以向我描述,这是怎么回事?该计划看起来非常糟糕!非常感谢你。

4 个答案:

答案 0 :(得分:1)

你没说多长时间。您正在加入一个800万行表。不确定source_tab中有多少行。

我注意到执行计划表明了destination_tab的全表扫描。 destination_tab表上的city列是否已编入索引?如果没有,请尝试添加索引。如果是,Oracle可能会忽略它,因为它知道它需要返回每个值,而destination_tab是驱动表。

无论您如何优化它,随着表的增长,这将始终降低性能,因为您通过从连接到另一个的同一个表中获取值来更新每一行。也就是说,您总是进行N次操作,其中N是destination_tab中的行数。

高级别问题/建议:

  1. 您是否每次都需要更新每一行?是否只有某些行可能会更改值?如果是这样,您能否以某种方式预测需要更新哪些行并限制对其的更新。
  2. 为什么有提示?如果性能发生变化,我会尝试删除提示。为您找到最佳方案是优化者的工作。通过使用提示,您告诉优化器如何完成其​​工作。你最好是对的。
  3. 您正在将destination_tab上的full_name列更新为同一行的name列。但是您通过表的连接获取名称列。从选择中取出并使用下面的内容可能会更快。这是猜测。这可能没关系。

    update destination_tab dest    
     set full_name = name,
       state = 
       (select src.state   
        from source_tab src   
        where src.city = dest.city);
    

答案 1 :(得分:1)

如果这是一个数据仓库,我不会做更新,尤其不是大表中的每一行。我可能会创建一个materialized view来组合各种基表中的各个部分,并在需要时进行完全刷新(非原子:truncate + insert append)。

编辑: 至于为什么当前的更新方法花费的时间比平时长得多,我的猜测就是在以前的运行中,Oracle在缓冲区缓存中发现了更新所需的大量块,最近Oracle不得不拉首先从磁盘到缓冲区很多。您可以查看一致的获取和db块获取(逻辑io)与物理io(磁盘)。

答案 2 :(得分:1)

尝试以下方法。

merge
 into destination_tab d
using source_tab      s
   on (d.city = d.city)
when matched then
   update 
      set d.state = s.state
    where decode(d.state, s.state, 1, 0) = 0;

答案 3 :(得分:0)

我理解有关数据仓库意义的评论等。但是,我必须在这种情况下进行此更新。此更新是ETL工作流程的一部分。我必须每个月复制表“目的地”的完整800万条记录。在这一步之后,我必须进行UPDATE,这会产生问题。

我不明白这个问题,日常表现如此糟糕。通常,更新运行45分钟。现在,它运行大约4个小时。但为什么?没有必要的分类,因此着名的原因是“在光盘上而不是在主存储器上分类”是不可能的。我的问题是什么问题?

正常更新(我是如何做)和合并更新之间的性能是否存在差异?