Oracle:批量收集性能

时间:2012-07-12 13:45:14

标签: oracle bulk

你能帮助我理解这句话吗?

  

如果没有批量绑定,PL / SQL会向SQL引擎发送一条SQL语句   为每个插入,更新或删除的记录导致   上下文切换会损害性能。

2 个答案:

答案 0 :(得分:18)

在Oracle中,有一个SQL虚拟机(VM)和一个PL / SQL VM。当您需要从一个VM移动到另一个VM时,会产生上下文转换的成本。单独地,这些上下文转换相对较快,但是当您进行逐行处理时,它们可以累计占用代码花费的大部分时间。当您使用批量绑定时,您可以通过单个上下文切换将多行数据从一个VM移动到另一个VM,从而显着减少上下文转换次数,从而加快代码的使用。

例如,使用显式游标。如果我写这样的东西

DECLARE
  CURSOR c 
      IS SELECT *
           FROM source_table;
  l_rec source_table%rowtype;
BEGIN
  OPEN c;
  LOOP
    FETCH c INTO l_rec;
    EXIT WHEN c%notfound;

    INSERT INTO dest_table( col1, col2, ... , colN )
      VALUES( l_rec.col1, l_rec.col2, ... , l_rec.colN );
  END LOOP;
END;

然后每次执行提取时,我都是

  • 执行从PL / SQL VM到SQL VM的上下文转换
  • 要求SQL VM执行游标以生成下一行数据
  • 执行从SQL VM返回到PL / SQL VM的另一个上下文切换以返回我的单行数据

每次插入一行,我都会做同样的事情。我承担了将一行数据从PL / SQL VM发送到SQL VM的上下文转换成本,要求SQL执行INSERT语句,然后将另一个上下文转换的成本转回PL / SQL。

如果source_table有100万行,那么400万次上下文转换可能占我代码经过时间的合理分数。另一方面,如果我执行BULK COLLECT LIMIT为100,我可以通过从SQL VM中检索100行数据到PL /中的集合来消除99%的上下文转换每当我产生上下文转换的成本并且每次在那里引入上下文转换时将100行插入到目标表中时,SQL都会出现。

如果可以重写我的代码以使用批量操作

DECLARE
  CURSOR c 
      IS SELECT *
           FROM source_table;
  TYPE  nt_type IS TABLE OF source_table%rowtype;
  l_arr nt_type;
BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO l_arr LIMIT 100;
    EXIT WHEN l_arr.count = 0;

    FORALL i IN 1 .. l_arr.count
      INSERT INTO dest_table( col1, col2, ... , colN )
        VALUES( l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN );
  END LOOP;
END;

现在,每次执行fetch时,我都会通过一组上下文切换将100行数据检索到我的集合中。每次我执行FORALL插入时,我都会插入100行,并使用一组上下文移位。如果source_table有100万行,这意味着我已经从400万个上下文转换到40,000个上下文转换。如果背景变化占我代码经过时间的20%,我已经消除了19.8%的经过时间。

您可以增加LIMIT的大小以进一步减少上下文转换的次数,但您很快就会达到收益递减规律。如果您使用的是LIMIT 1000而不是100,那么您将消除99.9%的上下文转换而不是99%。这意味着你的收藏品使用了10倍以上的PGA内存。在我们的假设示例中,它只会消耗0.18%的经过时间。通过消除额外的上下文转换,您可以很快达到使用额外内存所节省的时间。一般来说,LIMIT介于100到1000之间可能是最佳选择。

当然,在这个例子中,消除所有上下文转换并在单个SQL语句中执行所有操作仍然会更有效

INSERT INTO dest_table( col1, col2, ... , colN )
  SELECT col1, col2, ... , colN
    FROM source_table;

如果您正在对源表中的数据进行某种操作,而在SQL中无法合理地实现,那么首先使用PL / SQL是有意义的。

另外,我故意在我的例子中使用了一个显式游标。如果您使用隐式游标,则在最新版本的Oracle中,您可以隐式地获得BULK COLLECT的{​​{1}}为100的好处。还有另一个StackOverflow问题讨论了相对performance benefits of implicit and explicit cursors with bulk operations的问题,该问题详细介绍了这些特殊的皱纹。

答案 1 :(得分:1)

据我所知,有两个引擎,PL/SQL engine and SQL Engine。执行一次使用一个引擎的查询比在两个

之间切换更有效

示例:

  INSERT INTO t VALUES(1)

由SQL引擎处理

  FOR Lcntr IN 1..20

  END LOOP

由PL / SQL引擎执行

如果将上面的两个语句合并,将INSERT放在循环中,

FOR Lcntr IN 1..20
  INSERT INTO t VALUES(1)
END LOOP

Oracle将在两个引擎之间切换,每次(20)次迭代。 在这种情况下,建议使用BULK INSERT,它在整个执行过程中都使用PL / SQL引擎