3

每个月我都会在我的 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

4 回答 4

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;
于 2013-03-17T22:05:42.597 回答
1

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

我注意到执行计划表明对destination_tab 进行全表扫描。destination_tab 表上的城市列是否已编入索引?如果没有,请尝试添加索引。如果是,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);
    
于 2013-03-17T16:51:08.783 回答
1

如果这是一个数据仓库,我不会进行更新,尤其是不会更新大表中的每一行。我可能会创建一个结合来自各种基表的片段的物化视图,并在需要时进行完全刷新(非原子:截断+插入追加)。

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

于 2013-03-17T18:32:12.963 回答
0

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

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

正常更新(我是怎么做的)和合并更新之间的性能会有所不同吗?

于 2013-03-17T21:53:08.733 回答