TypeORM 中的内存问题,或者我只是把它填满

Memory problems in TypeORM, or me just filling it up

使用 typeorm 在 typescript 中编写了一个数据库初始化脚本。而且我似乎导致了内存问题,但我想不出解决办法。

目前脚本导入三个文件 配置文件(55 条记录) 用户(5306条记录) 登录(1006909 条记录)

重写了所有情况下的调用,脚本将创建一个包含所有更新的 JSON,然后使用 createQueryBuilder 执行更新,如下所示:

getConnection()
                                    .createQueryBuilder()
                                    .insert()
                                    .into(EULogin)
                                    .values(loginChunk)
                                    .execute()
                                    .catch(error => console.log(error))

对第一个有效,但到了最后一个(1,000,000个整数),它就不能玩了。我遇到了内存问题

LOGIN: Committed 196000/1006909 (Chunk Size:500) to database in 155 MS LOGIN: Committed 196500/1006909 (Chunk Size:500) to database in 823 MS

<--- Last few GCs --->

[60698:0x110008000] 34328 ms: Scavenge 1389.1 (1423.6) -> 1388.6 (1423.6) MB, 12.1 / 0.0 ms (average mu = 0.104, current mu = 0.099) allocation failure [60698:0x110008000] 34339 ms: Scavenge 1389.3 (1423.6) -> 1388.9 (1423.6) MB, 10.4 / 0.0 ms (average mu = 0.104, current mu = 0.099) allocation failure [60698:0x110008000] 34361 ms: Scavenge 1389.4 (1423.6) -> 1389.1 (1424.1) MB, 12.5 / 0.0 ms (average mu = 0.104, current mu = 0.099) allocation failure

<--- JS stacktrace --->

==== JS stack trace =========================================

0: ExitFrame [pc: 0x20ed7445be3d]
1: StubFrame [pc: 0x20ed7440d608]
2: StubFrame [pc: 0x20ed7502c4cc] Security context: 0x0fbfb411e6e9 <JSObject>
3: /* anonymous */(aka /* anonymous */) [0xfbf79c62a41] [/Users/bengtbjorkberg/Development/EUGrapherNode/node_modules/typeorm/query-builder/InsertQueryBuilder.js:348]

[bytecode=0xfbf93b851a1 offset=26](this=0x0fbfab4826f1 ,valueSet=0x0fbf8846ebc1 <Object map = 0xfbfbf7...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory 1: 0x10003cf99 node::Abort() [/usr/local/bin/node] 2: 0x10003d1a3 node::OnFatalError(char const*, char const*) [/usr/local/bin/node] 3: 0x1001b7835 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node] 4: 0x100585682 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/usr/local/bin/node] 5: 0x100588155 v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/usr/local/bin/node] 6: 0x100583fff v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/local/bin/node] 7: 0x1005821d4 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node] 8: 0x10058ea6c v8::internal::Heap::AllocateRawWithLigthRetry(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/usr/local/bin/node] 9: 0x10058eaef v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/usr/local/bin/node] 10: 0x10055e434 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [/usr/local/bin/node] 11: 0x1007e6714 v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node] 12: 0x20ed7445be3d

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

我试过以同步模式(不是异步)打开数据库 我试图一次将最后一次更新拆分为 50 条记录 我什至尝试为每个块打开和关闭数据库,但是因为我无法让它同步这样做而死了。

打开数据库:

createConnection().then(connection => {

下面是“Chunkloader”

.on('end', () => {
                            let compTime: number = new Date().getTime()
                            console.log("LOGIN: Entities read: " + loginReadCounter + " in " + new Date(compTime - loginStartTime).getMilliseconds() + " MS")
                            let currentChunk :number = 0;
                            let chunkSize : number = 500;
                            let loginChunk = [];
                            let loginStartChunkTime : number = compTime;
                            loginEntries.forEach(entry => {
                                loginChunk.push(entry);
                                currentChunk ++;
                                loginCommitCounter++;
                                if (currentChunk === chunkSize !! ){
                                    getConnection()
                                        .createQueryBuilder()
                                        .insert()
                                        .into(EULogin)
                                        .values(loginChunk)
                                        .execute()
                                        .catch(error => console.log(error))

                                    let compTime: number = new Date().getTime()
                                    console.log("LOGIN: Committed "  + loginCommitCounter + "/" + loginReadCounter + " (Chunk Size:" + loginChunk.length + ") to database in " + new Date(compTime - loginStartChunkTime).getMilliseconds() + " MS");
                                    currentChunk = 0;
                                    loginStartChunkTime = compTime;
                                    loginChunk = [];

                                }
                            });

有什么想法吗?

========================== 编辑好输入 =============== =====

为了理清头绪,我将它移到了一个单独的函数中,await 可以正常工作,但是如何在调用后停止该过程继续进行。因为 await 在 createConnection 内部工作,但它对 createConnection 不起作用,所以该函数将 return 直接

function syncDataWrite(dbEntitiy, dataSet){
    console.log("DBLOADER Started for: " + dataSet.length);
    createConnection().then(async connection => {
        console.log("DBLOADER Connected!");
        const completion = await createQueryBuilder()
            .insert()
            .into(dbEntitiy)
            .values(dataSet)
            .execute()
            .catch(error => console.log(error))
            console.log("DBLOADER SQL uploaded")
    })
    // console.log(dbEntity);

}

你的代码表明,当你分块时,你正在并行执行一切,这可能不是你想要的。

我建议你重写它以正确地按顺序排列它。

到目前为止最简单的方法是切换到 async/await 并使用常规 for 循环。假设 .execute() returns 一个承诺,这看起来像这样:

const connection = getConnection();
for (const entry of loginEntries) {
   // [snip]  
   await createQueryBuilder()
     .insert()
     .into(EULogin)
     .values(loginChunk)
     .execute()
   // [snip]
}

我删除了一堆你的代码,但我希望这个一般设置仍然有意义。

没有 async/await 也可以做到这一点,但它看起来要复杂得多。

根据您的编辑进行编辑

这是 syncDataWrite 的重写版本:

async function syncDataWrite(dbEntitiy, dataSet){

    console.log("DBLOADER Started for: " + dataSet.length);
    const connection = await createConnection();
    console.log("DBLOADER Connected!");
    const completion = await createQueryBuilder()
       .insert()
       .into(dbEntitiy)
       .values(dataSet)
       .execute();

}

注意,如果多次使用syncDataWrite,每个chunk 1次,调用时仍需要awaitsyncDataWrite。

我花了很多时间来解决这个性能问题, 在一天结束时,我使用存储库而不是 queryBuilder 让它工作。

之前:

ActiveRentalEntity.createQueryBuilder('ar')
  .insert()
  .values(data)
  .execute()

之后(使用存储库)。这不是因为分块的问题,我尝试使用查询生成器手动分块,但遇到了同样的问题,似乎存储库得到了更好的优化:

getRepository(ActiveRentalEntity).save(data, { chunk: 1000 });