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次,调用时仍需要await
syncDataWrite。
我花了很多时间来解决这个性能问题,
在一天结束时,我使用存储库而不是 queryBuilder 让它工作。
之前:
ActiveRentalEntity.createQueryBuilder('ar')
.insert()
.values(data)
.execute()
之后(使用存储库)。这不是因为分块的问题,我尝试使用查询生成器手动分块,但遇到了同样的问题,似乎存储库得到了更好的优化:
getRepository(ActiveRentalEntity).save(data, { chunk: 1000 });
使用 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次,调用时仍需要await
syncDataWrite。
我花了很多时间来解决这个性能问题, 在一天结束时,我使用存储库而不是 queryBuilder 让它工作。
之前:
ActiveRentalEntity.createQueryBuilder('ar')
.insert()
.values(data)
.execute()
之后(使用存储库)。这不是因为分块的问题,我尝试使用查询生成器手动分块,但遇到了同样的问题,似乎存储库得到了更好的优化:
getRepository(ActiveRentalEntity).save(data, { chunk: 1000 });