gpt4 book ai didi

Oracle:批量收集性能

转载 作者:行者123 更新时间:2023-12-04 05:13:57 25 4
gpt4 key购买 nike

你能帮我理解这句话吗?

Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each record that is inserted, updated, or deleted leading to context switches that hurt performance.

最佳答案

在 Oracle 内部,有一个 SQL 虚拟机 (VM) 和一个 PL/SQL VM。当您需要从一个 VM 移动到另一个 VM 时,会产生上下文转换的成本。单独而言,这些上下文转换相对较快,但是当您进行逐行处理时,它们加起来会占代码花费时间的很大一部分。当您使用批量绑定(bind)时,您可以通过一次上下文转移将多行数据从一个 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 COLLECTLIMIT 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 行数据检索到我的集合中。每次我做我的 FORALL插入,我正在插入 100 行,其中包含一组上下文转换。如果 source_table有 100 万行,这意味着我已经从 400 万个上下文转换变为 40,000 个上下文转换。如果上下文转换占了我的代码运行时间的 20%,那么我已经消除了 19.8% 的运行时间。

    您可以增加 LIMIT 的大小进一步减少上下文转换的数量,但您很快就会遇到 yield 递减规律。如果您使用了 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 的好处。与 LIMIT 100 隐含。还有另一个 StackOverflow 问题讨论了相对的 performance benefits of implicit and explicit cursors with bulk operations更详细地介绍了那些特定的皱纹。

    关于Oracle:批量收集性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11453215/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com