Oracle:批量收集性能

Oracle:批量收集性能,oracle,bulk,Oracle,Bulk,你能帮我理解这个短语吗 如果没有大容量绑定,PL/SQL将向SQL引擎发送SQL语句 对于插入、更新或删除导致 影响性能的上下文切换 据我所知,这涉及到两个引擎。执行一次使用一个引擎的查询比在两个引擎之间切换更有效 例如: INSERT INTO t VALUES(1) 由SQL引擎在 FOR Lcntr IN 1..20 END LOOP 由PL/SQL引擎执行 如果将上述两条语句组合在一起,将INSERT放入循环中 FOR Lcntr IN 1..20 INSERT I

你能帮我理解这个短语吗

如果没有大容量绑定,PL/SQL将向SQL引擎发送SQL语句 对于插入、更新或删除导致 影响性能的上下文切换


据我所知,这涉及到两个引擎。执行一次使用一个引擎的查询比在两个引擎之间切换更有效

例如:

  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
对于每(20)次迭代,Oracle将在两个引擎之间切换。
在这种情况下,建议使用大容量插入,它在Oracle中的整个执行过程中都使用PL/SQL引擎,有一个SQL虚拟机(VM)和一个PL/SQL虚拟机。当您需要从一个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的上下文转换
  • 要求SQLVM执行游标以生成下一行数据
  • 执行另一个从SQLVM到PL/SQLVM的上下文转换,以返回我的单行数据
每次我插入一行,我都在做同样的事情。我将承担上下文转移的成本,将一行数据从PL/SQL VM发送到SQL VM,要求SQL执行
INSERT
语句,然后将另一个上下文转移回PL/SQL

如果
source_table
有100万行,那么就有400万个上下文移位,这可能占我代码运行时间的合理比例。另一方面,如果我使用100的
限制执行
大容量收集
,我可以消除99%的上下文转移,方法是每次发生上下文转移时,从SQL VM中检索100行数据到PL/SQL中的集合中,并在每次发生上下文转移时将100行插入目标表中

如果你能重写我的代码来使用批量操作

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行数据检索到我的集合中。每次我对所有
insert执行
FORALL时,我都会用一组上下文移位插入100行。如果
source_table
有一百万行,这意味着我已经从400万个上下文转移到40000个上下文转移。如果上下文转移占代码运行时间的20%,那么我已经消除了19.8%的运行时间

您可以增加
限制的大小
以进一步减少上下文转移的数量,但很快就会达到收益递减的规律。如果使用1000而不是100的
限制,则可以消除99.9%的上下文移位,而不是99%。这意味着你的收藏使用了10倍以上的PGA内存。在我们假设的例子中,它只会减少0.18%的运行时间。您很快就会达到这样一个点:您正在使用的额外内存所增加的时间比您通过消除额外的上下文转移所节省的时间还要多。一般来说,介于100和1000之间的
限制可能是最佳点

当然,在本例中,消除所有上下文转移并在单个SQL语句中完成所有操作将更加有效

INSERT INTO dest_table( col1, col2, ... , colN )
  SELECT col1, col2, ... , colN
    FROM source_table;
如果您正在对源表中的数据进行某种操作,而这些操作无法在SQL中合理实现,那么首先使用PL/SQL才有意义


此外,我在示例中故意使用了显式游标。如果您使用的是隐式游标,那么在Oracle的最新版本中,您将获得
批量收集
的好处,隐式限制为100。还有另外一个问题是关于亲戚的,这个问题更详细地讨论了这些皱纹。

你的最后一句话有点骗人。批量使上下文切换只发生一次,尽管它仍然发生。