如何在批处理模式下运行存储过程或在并行处理中运行它

Posted

技术标签:

【中文标题】如何在批处理模式下运行存储过程或在并行处理中运行它【英文标题】:how to run the stored procedure in batch mode or in run it in parallel processing 【发布时间】:2018-02-20 03:12:56 【问题描述】:

我们正在从全局临时表中迭代 100k+ 条记录。下面的存储过程将从 glogal 临时表中一一迭代所有记录,并且必须处理以下三个步骤。

    查看产品是否存在 查看资产中的产品是否属于“类别”。 查看资产的文件名是否以“%pdf%”开头。

因此,每条记录都必须处理这 3 个步骤,并且最终的文档名称将存储在表中以获取成功的记录。如果任何步骤中出现任何错误,则会为该记录存储错误消息。

下面的存储过程需要很长时间来处理,因为它是按顺序处理的。

    有没有办法通过批处理来使存储过程本身的这个过程更快? 如果在存储过程中不可能,那么我们可以将此代码更改为 Java 并在多线程模式下运行此代码吗?就像创建 10 个线程,每个线程将同时获取一条记录并处理此代码。如果有人提供一些伪代码,我会很高兴。

建议采用哪种方法?

DECLARE
V_NODE_ID  VARCHAR2(20);
V_FILENAME VARCHAR2(100);
V_CATEGORY_COUNT INTEGER :=0;  
FINAL_FILNAME VARCHAR2(2000);
V_FINAL_ERRORMESSAGE VARCHAR2(2000);


CURSOR C1 IS
SELECT isbn FROM GT_ADD_ISBNS GT;

CURSOR C2(v_isbn in varchar2) IS
SELECT ANP.NODE_ID NODE_ID
        FROM 
        table1 ANP,
        table2 ANPP,
        table3 AN
        WHERE 
      ANP.NODE_ID=AN.ID AND
    ANPP.NODE_ID=ANP.NODE_ID AND
    AN.NAME_ID =26 AND
    ANP.CATEORGY='category' AND
    ANP.QNAME_ID='categories'  AND
        ANP.NODE_ID IN(SELECT CHILD_NODE_ID 
                  FROM TABLE_ASSOC START WITH PARENT_NODE_ID IN(v_isbn) 
                      CONNECT BY PRIOR CHILD_NODE_ID = PARENT_NODE_ID);


BEGIN
--Iterating all Products
FOR R1 IN C1 
LOOP

FINAL_FILNAME :='';
BEGIN


--To check whether Product is exists or not
SELECT AN.ID INTO V_NODE_ID 
FROM TABLE1 AN,
TABLE2 ANP
WHERE
AN.ID=ANP.NODE_ID AND
ANP.VALUE in(R1.ISBN);


V_CATEGORY_COUNT :=0;
V_FINAL_ERRORMESSAGE :='';

--To check Whether Product inside the assets are having the 'category' is applied or not
FOR R2 IN C2(R1.ISBN) 
LOOP

V_CATEGORY_COUNT := V_CATEGORY_COUNT+1;  

BEGIN
--In this Logic Product inside the assets have applied the 'category' But those assets are having documents LIKE '%pdf%' or not
SELECT ANP.STRING_VALUE  into V_FILENAME
        FROM 
        table1 ANP,
        table2 ANPP,
        table3 ACD
        WHERE 
       ANP.QNAME_ID=21  AND 
       ACD.ID=ANPP.LONG_VALUE 
       ANP.NODE_ID=ANPP.NODE_ID AND
       ANPP.QNAME_ID=36 AND
       ANP.STRING_VALUE LIKE '%pdf%'  AND 
       ANP.NODE_ID=R2.NODE_ID; 

    FINAL_FILNAME := FINAL_FILNAME  || V_FILENAME ||',';

   EXCEPTION WHEN
     NO_DATA_FOUND THEN
     V_FINAL_ERRORMESSAGE:=V_FINAL_ERRORMESSAGE|| 'Category is applied for this Product But for the asset:'||  R2.NODE_ID || ':Documents[LIKE %pdf%] were not found ;';
     UPDATE GT_ADD_ISBNS SET ERROR_MESSAGE=  V_FINAL_ERRORMESSAGE  WHERE ISBN= R1.ISBN;


     END;--Iterating for each NODEID

END LOOP;--Iterating the assets[Nodes] for each product of catgeory

  --  DBMS_OUTPUT.PUT_LINE('R1.ISBN:' || R1.ISBN ||'::V_CATEGORY_COUNT:' || V_CATEGORY_COUNT);

 IF(V_CATEGORY_COUNT  = 0) THEN
     UPDATE GT_ADD_ISBNS SET ERROR_MESSAGE=  'Category is not applied to none of the Assets for this Product'  WHERE ISBN= R1.ISBN;
   END IF;  


EXCEPTION WHEN
NO_DATA_FOUND THEN
      UPDATE GT_ADD_ISBNS SET ERROR_MESSAGE=   'Product is not Found:' WHERE ISBN= R1.ISBN;
END;

  -- DBMS_OUTPUT.PUT_LINE( R1.ISBN || 'Final documents:'||FINAL_FILNAME);
      UPDATE GT_ADD_ISBNS SET FILENAME=FINAL_FILNAME WHERE ISBN= R1.ISBN;

COMMIT;
END LOOP;--looping gt_isbns
END;

【问题讨论】:

在临时表中添加额外的“部分”列,并以循环方式为所有记录分配数字 1-10。将存储过程更改为仅处理 1 个部分。现在对每个部分并行运行存储过程 10 次。 更好的是,调整您的 SQL。处理 100k+ 条记录不应该花费“很长时间来处理”,尽管您没有定义“长”的含义。 完成此过程需要 15-16 小时。请注意,查询逻辑是正确的,并且在列上使用了所需的连接和所需的索引。在添加部分列和添加数字之后。我需要更改存储过程中的任何内容? 【参考方案1】:

您有许多潜在的性能影响。这是一个:

“我们正在从全局临时表中迭代 100k+ 条记录”

全局临时表可能非常慢。填充它们意味着将所有数据写入磁盘;从它们读取意味着从磁盘读取。这是可以避免的大量 I/O。此外,GTT 使用 临时表空间,因此您可能会与其他执行大型排序的会话发生争用。

这是另一个危险信号:

FOR R1 IN C1 LOOP ... FOR R2 IN C2(R1.ISBN) LOOP

SQL 是一种基于集合的语言。它针对以高性能方式连接表和返回数据集进行了优化。嵌套游标循环意味着逐行处理,这无疑更容易编码,但可能比等效的集合操作慢几个数量级。

--To check whether Product is exists or not

您有多个从同一个表中选择的查询(AN、'ANP) using the same criteria (isbn`)。也许所有这些重复项是验证您的业务规则的唯一方法,但似乎不太可能。

FINAL_FILNAME := FINAL_FILNAME || V_FILENAME ||',';

也许您可以重写查询以使用 listagg() 而不是使用过程逻辑来连接字符串?

UPDATE GT_ADD_ISBNS

同样,您的所有更新都是单行操作,而不是集合操作。

“有没有什么办法可以通过批处理来使存储过程本身的这个过程更快?”

在不了解您的规则和上下文的情况下,我们无法为您重写您的逻辑,但 15-16 小时对于此来说太长了,因此您绝对可以减少经过的时间。

需要考虑的事项:

    用您用来填充临时表的查询替换对临时表的写入和读取 重写循环以使用具有高 LIMIT(例如 1000)的 BULK COLLECT 来提高选择效率。 Find out more。 填充数组并使用 FORALL 来提高更新效率。 Find out more。 尝试通过将逻辑合并到主查询中来删除所有这些单独的查找,使用 OUTER JOIN 语法来测试是否存在。

这些都是猜测。如果您真的想知道程序将时间花在哪里——而这种知识是所有成功调优的根源,所以您应该想知道——您应该在 PL/SQL Profiler 下运行该程序。这将告诉您哪些线路花费的时间最多,而这些线路通常是您需要集中精力进行调整的线路。如果您还没有访问 DBMS_PROFILER 的权限,您将需要一个 DBA 来为您运行安装脚本。 Find out more.

"我们可以把这段代码改成Java并在多线程模式下运行这段代码吗?"

鉴于减慢过程的原因之一是从临时表中选择的 I/O 成本,因此多线程很有可能会引入进一步的争用,实际上会使事情变得更糟。您应该首先寻求改进存储过程。

【讨论】:

以上是关于如何在批处理模式下运行存储过程或在并行处理中运行它的主要内容,如果未能解决你的问题,请参考以下文章

并行运行存储过程

我们如何避免并行执行存储过程?

多处理 Python 3

并行LINQ PLinq

学习总结之ARM处理器的运行模式及ARM寄存器

Camerax 在服务中运行。如何在前台服务中获取生命周期所有者或在没有它的情况下运行?