为什么在使用 Node.js 将数千行插入 Oracle 表时会发生这种情况?

问题描述

我是 Oracle 的新手,我正在寻找将数千(可能数百万)条记录插入到表中的最佳方法

我已经看到有关这种情况的其他问题和答案,但在此 answer 中,PL/sql 代码使用了两个标量类型 (PSL_INTEGER) 关联数组,并用作表列,我需要相同的但有一个记录/复杂类型的嵌套表,用于在表中作为一行插入。

首先,我使用 oracledb 包 (v 5.1.0) 在 Node.js (TypeScript) 中有此代码

let data: Array<DataModel>;

// data's variable is populated with data and 'DataModel' is an interface,// data is an array with a the exact table's structure: 
// [ 
//     { C_ONE: 'mike',C_TWO: 'hugman',C_THREE: '34',... with other 12 columns },//     { C_ONE: 'robert',C_TWO: 'zuck',//     { C_ONE: 'john',C_TWO: 'gates',... with other 12 columns } 
// ]

let context;

try {
    
    context = await oracledb.getConnection({
        user: 'admin',password: 'admin',connectString: 'blabla'
    });

    const result = await context.execute(
        // My SP
        'BEGIN PACKAGE_TEST.SP_TEST_STRESS(:p_data,:p_status); END;',{
            // My JSON Array
            p_data: {
                type: 'PACKAGE_TEST.T_STRESS',val: data
            },// Variable for check if all success or fails... this doesn't matters :)
            p_status: {
                type: oracledb.NUMBER,val: 1,dir: oracledb.BIND_OUT
            }
        },{ autoCommit: true }
    );

    console.log(result);

    if ((result.outBinds as { p_status: number }).p_status === 0) {
        // Correct
    }
    else {
        // Failed
    }

} catch (error) {
    
    // bla bla for errors

} finally {

    if (context) {
        
        try {

            await context.close();
            
        } catch (error) {

            // bla bla for errors

        }

    }

}

以及我的 sotore 过程的 PL/sql 代码

CREATE OR REPLACE PACKAGE PACKAGE_TEST
IS
    
    TYPE R_STRESS IS RECORD
    (
        C_ONE VARCHAR(50),C_TWO VARCHAR(500),C_THREE VARCHAR(10),C_FOUR VARCHAR(100),C_FIVE VARCHAR(10),C_SIX VARCHAR(100),C_SEVEN VARCHAR(50),C_EIGHT VARCHAR(50),C_NINE VARCHAR(50),C_TEN VARCHAR(50),C_ELEVEN VARCHAR(50),C_TWELVE VARCHAR(50),C_THIRTEEN VARCHAR(300),C_FOURTEEN VARCHAR(100),C_FIVETEEN VARCHAR(300),C_SIXTEEN VARCHAR(50)
    );
    
    TYPE T_STRESS IS VARRAY(213627) OF R_STRESS;
    
    PROCEDURE SP_TEST_STRESS
    (
        P_DATA_FOR_PROCESS T_STRESS,P_STATUS OUT NUMBER
    );
    
END;

/

CREATE OR REPLACE PACKAGE BODY PACKAGE_TEST
IS

    PROCEDURE SP_TEST_STRESS
    (
        P_DATA_FOR_PROCESS T_STRESS,P_STATUS OUT NUMBER
    )
    IS
    BEGIN
    
        DBMS_OUTPUT.put_line('started');
        
        BEGIN
        
            FORALL i IN 1 .. P_DATA_FOR_PROCESS.COUNT
        
                INSERT INTO TEST_STRESS
                (
                    C_ONE,C_TWO,C_THREE,C_FOUR,C_FIVE,C_SIX,C_SEVEN,C_EIGHT,C_NINE,C_TEN,C_ELEVEN,C_TWELVE,C_THIRTEEN,C_FOURTEEN,C_FIVETEEN,C_SIXTEEN
                )
                VALUES
                (
                    P_DATA_FOR_PROCESS(i).C_ONE,P_DATA_FOR_PROCESS(i).C_TWO,P_DATA_FOR_PROCESS(i).C_THREE,P_DATA_FOR_PROCESS(i).C_FOUR,P_DATA_FOR_PROCESS(i).C_FIVE,P_DATA_FOR_PROCESS(i).C_SIX,P_DATA_FOR_PROCESS(i).C_SEVEN,P_DATA_FOR_PROCESS(i).C_EIGHT,P_DATA_FOR_PROCESS(i).C_NINE,P_DATA_FOR_PROCESS(i).C_TEN,P_DATA_FOR_PROCESS(i).C_ELEVEN,P_DATA_FOR_PROCESS(i).C_TWELVE,P_DATA_FOR_PROCESS(i).C_THIRTEEN,P_DATA_FOR_PROCESS(i).C_FOURTEEN,P_DATA_FOR_PROCESS(i).C_FIVETEEN,P_DATA_FOR_PROCESS(i).C_SIXTEEN
                );
        
        EXCEPTION
        WHEN OTHERS THEN
            p_status := 1;
        END;
        
        P_STATUS := 0;
        
    END;

END;

还有我的目标表:

CREATE TABLE TEST_STRESS
(
    C_ONE VARCHAR(50),C_SIXTEEN VARCHAR(50)
);

在这种情况下会发生有趣的行为:

  • 如果我发送包含 200 行的 JSON 数组,则效果很好,我不知道成功完成所需的确切时间,但是 我可以说是毫秒。

  • 如果我发送包含 200,000 行的 JSON 数组,这需要三到四分钟的等待时间,promise 得到解决并抛出一个类型的异常:ORA-04036:实例超过 PGA_AGGREGATE_LIMIT

enter image description here

这个是在将JSON数组传递给procedure参数的时候出现的,看来处理的时候开销太大了。

  • 为什么在第二种情况下会发生这种情况?
  • 是否有限制 nesTED TABLE TYPES 中的行数还是Node.js 的任何配置(认)?
  • Oracle 建议 增加 pga_aggregate_limit 但在我的 sqlDeveloper 中看到它 "show parameter pga;" 是3G,是不是表示我的信息 发送是否超过 3 GB?正常吗?
  • 是否有更可行的解决方案 不影响数据库性能

感谢您的帮助。

解决方法

每个服务器进程都有自己的 PGA,所以我猜这会导致当前运行的所有进程的总聚合 PGA 超过 3 GB。

我认为这是因为您的包裹内部发生了什么,但您只显示了规范,因此无法判断那里发生了什么。

您没有使用嵌套表类型。您正在使用 varray。 varray 的 maximum length 为 2,147,483,647。

听起来您在程序内部执行了一些操作以使用过多内存。也许您需要分块处理 200,000 行?如果没有关于您要执行的操作的更多信息,您是否可以使用其他一些进程来加载数据,例如 sqlldr?