2013-03-17 93 views
3

每个月我在oracle数据库上做一个简单的更新语句。但是,从星期一开始需要很长时间。该表每个月增长5%。现在有800万条记录被存储。简单的Oracle UPDATE语句异常糟糕的性能

陈述:

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个领域的更新,而不是只有两个......但这样看起来更容易descripe问题。

解释计划:

-----------------------------------------------------------------------------------------------------        
| 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 | 
----------------------------------------------------------------------------------------------------- 

谁能descripe给我,这怎么可能呢?该计划看起来非常糟糕!非常非常感谢你。

+2

你为什么要这样做?每个月你更新你的800万行中的每一行?这永远不会很快,而且肯定是不必要的。一旦你更新了行,是否真的需要每个月再次更新一次?如果您希望所有行都具有最新的源信息,那么为什么不要在source_tab(join)中选择它,以查看它并且根本不将它复制到destination_tab? – 2013-03-17 16:42:27

+0

当你说这个计划“看起来很糟糕”时,我猜你的意思是DESTINATION_TAB上的全表访问?错,那很好! – 2013-03-17 16:43:10

+0

源表中有多少行(大约)? (有统计信息吗?) – Mat 2013-03-17 16:43:21

回答

1

你没有说多久太久了。你正在加入一个800万行的表格。不知道source_tab中有多少行。

我注意到执行计划指出了destination_tab的全表扫描。 destination_tab表上的城市列是否已编入索引?如果不是,请尝试添加索引。如果是这样,Oracle可能会忽略它,因为它知道它无论如何都需要返回每个值,而destination_tab是驱动表。

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

高层次的问题/建议:

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

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

尽管我通常同意避免提示并让优化器完成工作,但并行提示有点不同。虽然他们告诉优化器要做什么,但他们也提供了有用的信息。并行提示是告诉优化器哪些语句更重要,并且应该使用超过其公平份额的资源的方式。 – 2013-03-17 22:29:41

1

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

编辑: 至于为什么目前的更新途径的时间比平常长得多,我猜测的是,在之前的运行Oracle找到了一个好号码需要在缓冲区高速缓存中的更新块,最近甲骨文已首先从磁盘拉入缓冲区。您可以查看一致的获取和db块获取(逻辑io)与物理io(磁盘)。

+0

我该如何测试这个? – Twixt3245 2013-03-18 23:56:37

+0

alter session set sql_trace = true,并使用tkprof检查输出。很好进入这里:http://www.oracle-base.com/articles/misc/sql-trace-10046-trcsess-and-tkprof.php – tbone 2013-03-19 11:04:03

0

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

我不明白这个问题,表现是如此糟糕的日常工作。通常,更新运行45分钟。现在,它运行约4个小时。但为什么?没有必要的排序,所以着名的理由是“在光盘上对主存储器进行排序”是不可能的。我的问题是什么?

对于正常更新(我是怎么做的)和合并更新之间的性能会有差异吗?

+0

你知道查询计划的样子吗?我猜不会。 – 2013-03-18 00:55:00

+0

没有抱歉,但我的意思是这个计划看起来不错。 destination_tab上的全表访问是可以的,因为我想更新每条记录。要在source_tab上搜索记录,oracle使用索引。我不知道问题是什么。 – Twixt3245 2013-03-18 23:55:49

+0

可能你之前有一个更好的查询计划,但你永远不会知道它是什么。无论如何,还有很多其他建议可供您尝试。我会建议尝试每个建议并记下查询计划。如果没有差别,性能就会有所不同(除非存在周期性硬件瓶颈) – 2013-03-19 00:17:27

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; 
+0

好吧我明天试试 - 我的英语水平如何? – Twixt3245 2013-03-17 22:13:45

+0

+1好主意。可能值得说明的是,MERGE有时可能会执行UPDATE,因为MERGE可以使用散列连接。 – 2013-03-17 22:21:16

+0

@ Twixt3245另外,如果你想并行运行,不要忘记运行'alter session enable parallel dml;'。如果您使用的是11gR2,请使用语句级并行机制,比如'merge/* + parallel(4)* /' - 那么您不需要指定对象。原始示例的表名拼写错误,不应使用并行性。 – 2013-03-17 22:23:54