NodeJS堆内存不足,需要进行长时间的数据库访问

时间:2018-08-01 16:18:47

标签: node.js

我正在使用Express 4 + Sequelize + Postgresql数据库构建NodeJs应用程序。 我正在使用Node v8.11.3。

我编写了一个脚本,用于将数据从JSON文件加载到数据库中。我用大约30个实体的样本对脚本进行了测试。效果很好。

实际上,在完整的JSON文件中,我要加载约100000个实体。我的脚本读取JSON文件,并尝试异步填充数据库(即同时填充10万个实体)。

结果是几分钟后:

<--- Last few GCs --->

[10488:0000018619050A20]   134711 ms: Mark-sweep 1391.6 (1599.7) -> 1391.6 (1599.7) MB, 1082.3 / 0.0 ms  allocation failure GC in old space requested
[10488:0000018619050A20]   136039 ms: Mark-sweep 1391.6 (1599.7) -> 1391.5 (1543.7) MB, 1326.9 / 0.0 ms  last resort GC in old space requested
[10488:0000018619050A20]   137351 ms: Mark-sweep 1391.5 (1543.7) -> 1391.5 (1520.2) MB, 1311.5 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

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

Security context: 0000034170025879 <JSObject>
    1: split(this=00000165BEC5DB99 <Very long string[1636]>)
    2: attachExtraTrace [D:\Code\backend-lymo\node_modules\bluebird\js\release\debuggability.js:~775] [pc=0000021115C5728E](this=0000003CA90FF711 <CapturedTrace map = 0000033AD0FE9FB1>,error=000001D3EC5EFD59 <Error map = 00000275F61BA071>)
    3: _attachExtraTrace(aka longStackTracesAttachExtraTrace) [D:\Code\backend-lymo\node_module...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node_module_register
 2: v8::internal::FatalProcessOutOfMemory
 3: v8::internal::FatalProcessOutOfMemory
 4: v8::internal::Factory::NewFixedArray
 5: v8::internal::HashTable<v8::internal::SeededNumberDictionary,v8::internal::SeededNumberDictionaryShape>::IsKey
 6: v8::internal::HashTable<v8::internal::SeededNumberDictionary,v8::internal::SeededNumberDictionaryShape>::IsKey
 7: v8::internal::StringTable::LookupString
 8: v8::internal::StringTable::LookupString
 9: v8::internal::RegExpImpl::Exec
10: v8::internal::interpreter::BytecodeArrayRandomIterator::UpdateOffsetFromIndex
11: 0000021115A043C1

最后,已经创建了一些实体,但是该过程显然崩溃了。 我知道此错误是由于内存造成的。

我的问题是:为什么Node不花时间来管理所有事务而又不会过度占用内存?是否有“队列”来限制此类爆炸?

我确定了一些解决方法:

  • 将种子分割成几个JSON文件
  • 使用--max_old_space_size = 8192选项使用更多的内存
  • 按顺序继续(使用同步调用)

但是这些解决方案都不令我满意。这让我为自己的应用程序的未来感到恐惧,因为该应用程序有时会管理生产中的长时间操作。

您怎么看?

2 个答案:

答案 0 :(得分:1)

使用async.eachOfLimit最多同时执行X次操作:

var async = require("async");

var myBigArray = [];
var X = 10; // 10 operations in same time at max

async.eachOfLimit(myBigArray, X, function(element, index, callback){

    // insert element
    MyCollection.insert(element, function(err){
       return callback(err);
    });

}, function(err, result){

    // all finished
    if(err){
       // do stg
    }
    else
    {
       // do stg
     }

});

答案 1 :(得分:0)

Node.js就是按照您所说的去做。如果进入某个大循环并启动大量数据库操作,那么这正是node.js试图做的。如果开始的操作太多而消耗太多的资源(内存,数据库资源,文件等),那么您将遇到麻烦。 Node.js不会为您管理。必须由您的代码来管理您同时进行多少次操作。

另一方面,node.js尤其擅长同时运行一堆异步操作,并且如果将其编码为具有多个操作,则通常会获得更好的端到端性能。一次去。您希望同时运行多少个,完全取决于特定的代码以及异步操作正在执行的操作。如果这是数据库操作,则可能取决于数据库以及最能同时处理多少个请求。

这里有一些参考资料,为您提供了控制一次执行多少操作的方法的思路,包括一些代码示例:

Make several requests to an API that can only handle 20 request a minute

Promise.all consumes all my RAM

Javascript - how to control how many promises access network in parallel

Fire off 1,000,000 requests 100 at a time

Nodejs: Async request with a list of URL

Loop through an api get request with variable URL

Choose proper async method for batch processing for max requests/sec

如果您显示了代码,我们可以更具体地建议哪种技术最适合您的情况。